Show a patch.

GET /api/1.1/patches/1646/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 1646,
    "url": "https://patchwork.libcamera.org/api/1.1/patches/1646/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/1646/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/1.1/projects/1/?format=api",
        "name": "libcamera",
        "link_name": "libcamera",
        "list_id": "libcamera_core",
        "list_email": "libcamera-devel@lists.libcamera.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20190710191708.13049-3-laurent.pinchart@ideasonboard.com>",
    "date": "2019-07-10T19:17:05",
    "name": "[libcamera-devel,3/6] libcamera: signal: Support cross-thread signals",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": false,
    "hash": "0f6e1f40e385915bf6e2f565ea89441983c52f37",
    "submitter": {
        "id": 2,
        "url": "https://patchwork.libcamera.org/api/1.1/people/2/?format=api",
        "name": "Laurent Pinchart",
        "email": "laurent.pinchart@ideasonboard.com"
    },
    "delegate": null,
    "mbox": "https://patchwork.libcamera.org/patch/1646/mbox/",
    "series": [
        {
            "id": 414,
            "url": "https://patchwork.libcamera.org/api/1.1/series/414/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=414",
            "date": "2019-07-10T19:17:03",
            "name": "[libcamera-devel,1/6] libcamera: Add thread support",
            "version": 1,
            "mbox": "https://patchwork.libcamera.org/series/414/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/1646/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/1646/checks/",
    "tags": {},
    "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 10DD261575\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Jul 2019 21:17:45 +0200 (CEST)",
            "from pendragon.ideasonboard.com (softbank126163157105.bbtec.net\n\t[126.163.157.105])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id D016B31C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Jul 2019 21:17:43 +0200 (CEST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1562786264;\n\tbh=taA4zZl5SZX1QHjkNWVgkjPKNlIHP10+mCstO3jRTs4=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=oBYuw3q5TIqLdUVum4Pwqc6M8+wniSepRIEPpeiITpkziooP+539tlQorEZ6NU9tE\n\tiBZ/fsNH9gwt/nhaO/iQaBnz9zf7mN1+CoCYXzHTyY0ST5MtX/bw/MmXAzKk6ezM++\n\tKe6l5WjmeQb/qvGCXmCDyg/HH0cvPw/lqcSr3nFY=",
        "From": "Laurent Pinchart <laurent.pinchart@ideasonboard.com>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Wed, 10 Jul 2019 22:17:05 +0300",
        "Message-Id": "<20190710191708.13049-3-laurent.pinchart@ideasonboard.com>",
        "X-Mailer": "git-send-email 2.21.0",
        "In-Reply-To": "<20190710191708.13049-1-laurent.pinchart@ideasonboard.com>",
        "References": "<20190710191708.13049-1-laurent.pinchart@ideasonboard.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[libcamera-devel] [PATCH 3/6] libcamera: signal: Support\n\tcross-thread signals",
        "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": "Wed, 10 Jul 2019 19:17:45 -0000"
    },
    "content": "Allow signals to cross thread boundaries by posting them to the\nrecipient through messages instead of calling the slot directly when the\nrecipient lives in a different thread.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n include/libcamera/signal.h      | 70 +++++++++++++++++++++++++++++----\n src/libcamera/include/message.h | 14 +++++++\n src/libcamera/message.cpp       | 22 +++++++++++\n src/libcamera/object.cpp        | 15 +++++++\n src/libcamera/signal.cpp        | 26 ++++++++++++\n 5 files changed, 139 insertions(+), 8 deletions(-)",
    "diff": "diff --git a/include/libcamera/signal.h b/include/libcamera/signal.h\nindex c8f3243eaf5a..04e8fe7eebe3 100644\n--- a/include/libcamera/signal.h\n+++ b/include/libcamera/signal.h\n@@ -8,6 +8,8 @@\n #define __LIBCAMERA_SIGNAL_H__\n \n #include <list>\n+#include <tuple>\n+#include <type_traits>\n #include <vector>\n \n #include <libcamera/object.h>\n@@ -16,6 +18,9 @@ namespace libcamera {\n \n template<typename... Args>\n class Signal;\n+class SlotBase;\n+template<typename... Args>\n+class SlotArgs;\n \n class SlotBase\n {\n@@ -27,6 +32,9 @@ public:\n \tvoid *obj() { return obj_; }\n \tbool isObject() const { return isObject_; }\n \n+\tvoid activatePack(void *pack);\n+\tvirtual void invokePack(void *pack) = 0;\n+\n protected:\n \tvoid *obj_;\n \tbool isObject_;\n@@ -35,24 +43,70 @@ protected:\n template<typename... Args>\n class SlotArgs : public SlotBase\n {\n+private:\n+#ifndef __DOXYGEN__\n+\t/*\n+\t * This is a cheap partial implementation of std::integer_sequence<>\n+\t * from C++14.\n+\t */\n+\ttemplate<int...>\n+\tstruct sequence {\n+\t};\n+\n+\ttemplate<int N, int... S>\n+\tstruct generator : generator<N-1, N-1, S...> {\n+\t};\n+\n+\ttemplate<int... S>\n+\tstruct generator<0, S...> {\n+\t\ttypedef sequence<S...> type;\n+\t};\n+#endif\n+\n public:\n+\tusing PackType = std::tuple<typename std::remove_reference<Args>::type...>;\n+\n \tSlotArgs(void *obj, bool isObject)\n \t\t: SlotBase(obj, isObject) {}\n \n+\tvoid invokePack(void *pack) override\n+\t{\n+\t\tinvokePack(pack, typename generator<sizeof...(Args)>::type());\n+\t}\n+\n+\ttemplate<int... S>\n+\tvoid invokePack(void *pack, sequence<S...>)\n+\t{\n+\t\tPackType *args = static_cast<PackType *>(pack);\n+\t\tinvoke(std::get<S>(*args)...);\n+\t\tdelete args;\n+\t}\n+\n+\tvirtual void activate(Args... args) = 0;\n \tvirtual void invoke(Args... args) = 0;\n-\n-protected:\n-\tfriend class Signal<Args...>;\n };\n \n template<typename T, typename... Args>\n class SlotMember : public SlotArgs<Args...>\n {\n public:\n+\tusing PackType = std::tuple<typename std::remove_reference<Args>::type...>;\n+\n \tSlotMember(T *obj, bool isObject, void (T::*func)(Args...))\n \t\t: SlotArgs<Args...>(obj, isObject), func_(func) {}\n \n-\tvoid invoke(Args... args) { (static_cast<T *>(this->obj_)->*func_)(args...); }\n+\tvoid activate(Args... args)\n+\t{\n+\t\tif (this->isObject_)\n+\t\t\tSlotBase::activatePack(new PackType{ args... });\n+\t\telse\n+\t\t\t(static_cast<T *>(this->obj_)->*func_)(args...);\n+\t}\n+\n+\tvoid invoke(Args... args)\n+\t{\n+\t\t(static_cast<T *>(this->obj_)->*func_)(args...);\n+\t}\n \n private:\n \tfriend class Signal<Args...>;\n@@ -66,7 +120,8 @@ public:\n \tSlotStatic(void (*func)(Args...))\n \t\t: SlotArgs<Args...>(nullptr, false), func_(func) {}\n \n-\tvoid invoke(Args... args) { (*func_)(args...); }\n+\tvoid activate(Args... args) { (*func_)(args...); }\n+\tvoid invoke(Args... args) {}\n \n private:\n \tfriend class Signal<Args...>;\n@@ -186,9 +241,8 @@ public:\n \t\t * disconnect operation, invalidating the iterator.\n \t\t */\n \t\tstd::vector<SlotBase *> slots{ slots_.begin(), slots_.end() };\n-\t\tfor (SlotBase *slot : slots) {\n-\t\t\tstatic_cast<SlotArgs<Args...> *>(slot)->invoke(args...);\n-\t\t}\n+\t\tfor (SlotBase *slot : slots)\n+\t\t\tstatic_cast<SlotArgs<Args...> *>(slot)->activate(args...);\n \t}\n };\n \ndiff --git a/src/libcamera/include/message.h b/src/libcamera/include/message.h\nindex 97c9b80ec0e0..db17d647c280 100644\n--- a/src/libcamera/include/message.h\n+++ b/src/libcamera/include/message.h\n@@ -10,6 +10,7 @@\n namespace libcamera {\n \n class Object;\n+class SlotBase;\n class Thread;\n \n class Message\n@@ -17,6 +18,7 @@ class Message\n public:\n \tenum Type {\n \t\tNone = 0,\n+\t\tSignalMessage = 1,\n \t};\n \n \tMessage(Type type);\n@@ -32,6 +34,18 @@ private:\n \tObject *receiver_;\n };\n \n+class SignalMessage : public Message\n+{\n+public:\n+\tSignalMessage(SlotBase *slot, void *pack)\n+\t\t: Message(Message::SignalMessage), slot_(slot), pack_(pack)\n+\t{\n+\t}\n+\n+\tSlotBase *slot_;\n+\tvoid *pack_;\n+};\n+\n } /* namespace libcamera */\n \n #endif /* __LIBCAMERA_MESSAGE_H__ */\ndiff --git a/src/libcamera/message.cpp b/src/libcamera/message.cpp\nindex 47caf44dc82d..66dd1e8bd618 100644\n--- a/src/libcamera/message.cpp\n+++ b/src/libcamera/message.cpp\n@@ -68,4 +68,26 @@ Message::~Message()\n  * \\return The message receiver\n  */\n \n+/**\n+ * \\class SignalMessage\n+ * \\brief A message carrying a Signal across threads\n+ */\n+\n+/**\n+ * \\fn SignalMessage::SignalMessage()\n+ * \\brief Construct a SignalMessage\n+ * \\param[in] slot The slot that the signal targets\n+ * \\param[in] pack The signal arguments\n+ */\n+\n+/**\n+ * \\var SignalMessage::slot_\n+ * \\brief The slot that the signal targets\n+ */\n+\n+/**\n+ * \\var SignalMessage::pack_\n+ * \\brief The signal arguments\n+ */\n+\n }; /* namespace libcamera */\ndiff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\nindex 695e6c11b3a4..1dfa159267fe 100644\n--- a/src/libcamera/object.cpp\n+++ b/src/libcamera/object.cpp\n@@ -10,6 +10,7 @@\n #include <libcamera/signal.h>\n \n #include \"log.h\"\n+#include \"message.h\"\n #include \"thread.h\"\n \n /**\n@@ -32,6 +33,10 @@ namespace libcamera {\n  * This allows implementing easy message passing between threads by inheriting\n  * from the 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+ * in another thread.\n+ *\n  * \\sa Message, Signal, Thread\n  */\n \n@@ -82,6 +87,16 @@ void Object::postMessage(std::unique_ptr<Message> msg)\n  */\n void Object::message(Message *msg)\n {\n+\tswitch (msg->type()) {\n+\tcase Message::SignalMessage: {\n+\t\tSignalMessage *smsg = static_cast<SignalMessage *>(msg);\n+\t\tsmsg->slot_->invokePack(smsg->pack_);\n+\t\tbreak;\n+\t}\n+\n+\tdefault:\n+\t\tbreak;\n+\t}\n }\n \n /**\ndiff --git a/src/libcamera/signal.cpp b/src/libcamera/signal.cpp\nindex 4cb85ecb0686..53c18535fee3 100644\n--- a/src/libcamera/signal.cpp\n+++ b/src/libcamera/signal.cpp\n@@ -7,6 +7,10 @@\n \n #include <libcamera/signal.h>\n \n+#include \"message.h\"\n+#include \"thread.h\"\n+#include \"utils.h\"\n+\n /**\n  * \\file signal.h\n  * \\brief Signal & slot implementation\n@@ -42,8 +46,30 @@ namespace libcamera {\n  * to the same slot. Duplicate connections between a signal and a slot are\n  * allowed and result in the slot being called multiple times for the same\n  * signal emission.\n+ *\n+ * When a slot belongs to an instance of the Object class, the slot is called\n+ * in the context of the thread that the object is bound to. If the signal is\n+ * emitted from the same thread, the slot will be called synchronously, before\n+ * Signal::emit() returns. If the signal is emitted from a different thread,\n+ * the slot will be called asynchronously from the object's thread's event\n+ * loop, after the Signal::emit() method returns, with a copy of the signal's\n+ * arguments. The emitter shall thus ensure that any pointer or reference\n+ * passed through the signal will remain valid after the signal is emitted.\n  */\n \n+void SlotBase::activatePack(void *pack)\n+{\n+\tObject *obj = static_cast<Object *>(obj_);\n+\n+\tif (Thread::current() == obj->thread()) {\n+\t\tinvokePack(pack);\n+\t} else {\n+\t\tstd::unique_ptr<Message> msg =\n+\t\t\tutils::make_unique<SignalMessage>(this, pack);\n+\t\tobj->postMessage(std::move(msg));\n+\t}\n+}\n+\n /**\n  * \\fn Signal::connect(T *object, void(T::*func)(Args...))\n  * \\brief Connect the signal to a member function slot\n",
    "prefixes": [
        "libcamera-devel",
        "3/6"
    ]
}