{"id":1580,"url":"https://patchwork.libcamera.org/api/1.1/patches/1580/?format=json","web_url":"https://patchwork.libcamera.org/patch/1580/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20190701232339.5191-4-laurent.pinchart@ideasonboard.com>","date":"2019-07-01T23:23:39","name":"[libcamera-devel,v4,3/3] libcamera: ipc: unix: Make socket operation asynchronous","commit_ref":"f137451817f47c0bfe59586afe5af7b51f8ccad4","pull_url":null,"state":"accepted","archived":false,"hash":"da90fc2b9150f72b7653bad48ea39f6e7f8bebfa","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/1.1/people/2/?format=json","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/1580/mbox/","series":[{"id":389,"url":"https://patchwork.libcamera.org/api/1.1/series/389/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=389","date":"2019-07-01T23:23:36","name":"libcamera: ipc: unix: Add a IPC mechanism based on Unix sockets","version":4,"mbox":"https://patchwork.libcamera.org/series/389/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/1580/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/1580/checks/","tags":{},"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 9E8A96157C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 01:24:04 +0200 (CEST)","from pendragon.bb.dnainternet.fi\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 3786253B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  2 Jul 2019 01:24:04 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1562023444;\n\tbh=TjI7zocRVzBpCg7ppR9TAcE9AoBnfn+yWr7PS48vf9s=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=s/RWXt5wZv6/9Gak2hpnYh1DwB3Vtz1eBi3ciyVUmYUFoM6MaAarHWqT9TzrVLnFN\n\tADpYg2iHgLVtme6ToVa5Un12Kf1rRz4j+sUAdkXq1189ddjZGhddlXAMIeKU8y1yIo\n\tDRQ40IdwOv6lw9VZTox2xtocq4aKWpIN/Pm99tzU=","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Tue,  2 Jul 2019 02:23:39 +0300","Message-Id":"<20190701232339.5191-4-laurent.pinchart@ideasonboard.com>","X-Mailer":"git-send-email 2.21.0","In-Reply-To":"<20190701232339.5191-1-laurent.pinchart@ideasonboard.com>","References":"<20190701232339.5191-1-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"[libcamera-devel] [PATCH v4 3/3] libcamera: ipc: unix: Make socket\n\toperation asynchronous","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":"Mon, 01 Jul 2019 23:24:04 -0000"},"content":"Blocking socket operation when receiving messages may lead to long\ndelays, and possibly a complete deadlock, if the remote side delays\nsending of the payload after the header, or doesn't send the payload at\nall. To avoid this, make the socket non-blocking and implement a simple\nstate machine to receive the header synchronously with the socket read\nnotification. The payload read is still synchronous with the receive()\nmethod to avoid data copies.\n\nSigned-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n---\n src/libcamera/include/ipc_unixsocket.h |  2 +\n src/libcamera/ipc_unixsocket.cpp       | 88 ++++++++++++++++++--------\n 2 files changed, 63 insertions(+), 27 deletions(-)","diff":"diff --git a/src/libcamera/include/ipc_unixsocket.h b/src/libcamera/include/ipc_unixsocket.h\nindex ef166d742554..03e9fe492bde 100644\n--- a/src/libcamera/include/ipc_unixsocket.h\n+++ b/src/libcamera/include/ipc_unixsocket.h\n@@ -49,6 +49,8 @@ private:\n \tvoid dataNotifier(EventNotifier *notifier);\n \n \tint fd_;\n+\tbool headerReceived_;\n+\tstruct Header header_;\n \tEventNotifier *notifier_;\n };\n \ndiff --git a/src/libcamera/ipc_unixsocket.cpp b/src/libcamera/ipc_unixsocket.cpp\nindex c11f116093c5..def08eef00f8 100644\n--- a/src/libcamera/ipc_unixsocket.cpp\n+++ b/src/libcamera/ipc_unixsocket.cpp\n@@ -7,6 +7,7 @@\n \n #include \"ipc_unixsocket.h\"\n \n+#include <poll.h>\n #include <string.h>\n #include <sys/socket.h>\n #include <unistd.h>\n@@ -49,10 +50,10 @@ LOG_DEFINE_CATEGORY(IPCUnixSocket)\n  * transporting entire payloads with guaranteed ordering.\n  *\n  * The IPC design is asynchronous, a message is queued to a receiver which gets\n- * notified that a message is ready to be consumed by a signal. The queuer of\n- * the message gets no notification when a message is delivered nor processed.\n- * If such interactions are needed a protocol specific to the users use-case\n- * should be implemented on top of the IPC objects.\n+ * notified that a message is ready to be consumed by the \\ref readyRead\n+ * signal. The sender of the message gets no notification when a message is\n+ * delivered nor processed. If such interactions are needed a protocol specific\n+ * to the users use-case should be implemented on top of the IPC objects.\n  *\n  * Establishment of an IPC channel is asymmetrical. The side that initiates\n  * communication first instantiates a local side socket and creates the channel\n@@ -64,7 +65,7 @@ LOG_DEFINE_CATEGORY(IPCUnixSocket)\n  */\n \n IPCUnixSocket::IPCUnixSocket()\n-\t: fd_(-1), notifier_(nullptr)\n+\t: fd_(-1), headerReceived_(false), notifier_(nullptr)\n {\n }\n \n@@ -89,7 +90,7 @@ int IPCUnixSocket::create()\n \tint sockets[2];\n \tint ret;\n \n-\tret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets);\n+\tret = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);\n \tif (ret) {\n \t\tret = -errno;\n \t\tLOG(IPCUnixSocket, Error)\n@@ -142,6 +143,7 @@ void IPCUnixSocket::close()\n \t::close(fd_);\n \n \tfd_ = -1;\n+\theaderReceived_ = false;\n }\n \n /**\n@@ -193,38 +195,38 @@ int IPCUnixSocket::send(const Payload &payload)\n  * \\param[out] payload Payload where to write the received message\n  *\n  * This method receives the message payload from the IPC channel and writes it\n- * to the \\a payload. It blocks until one message is received, if an\n- * asynchronous behavior is desired this method should be called when the\n- * readyRead signal is emitted.\n+ * to the \\a payload. If no message payload is available, it returns\n+ * immediately with -EAGAIN. The \\ref readyRead signal shall be used to receive\n+ * notification of message availability.\n  *\n  * \\todo Add state machine to make sure we don't block forever and that\n  * a header is always followed by a payload.\n  *\n  * \\return 0 on success or a negative error code otherwise\n+ * \\retval -EAGAIN No message payload is available\n+ * \\retval -ENOTCONN The socket is not connected (neither create() nor bind()\n+ * has been called)\n  */\n int IPCUnixSocket::receive(Payload *payload)\n {\n-\tHeader hdr;\n-\tint ret;\n-\n \tif (!isBound())\n \t\treturn -ENOTCONN;\n \n-\tif (!payload)\n-\t\treturn -EINVAL;\n+\tif (!headerReceived_)\n+\t\treturn -EAGAIN;\n \n-\tret = ::recv(fd_, &hdr, sizeof(hdr), 0);\n-\tif (ret < 0) {\n-\t\tret = -errno;\n-\t\tLOG(IPCUnixSocket, Error)\n-\t\t\t<< \"Failed to recv header: \" << strerror(-ret);\n+\tpayload->data.resize(header_.data);\n+\tpayload->fds.resize(header_.fds);\n+\n+\tint ret = recvData(payload->data.data(), header_.data,\n+\t\t\t   payload->fds.data(), header_.fds);\n+\tif (ret < 0)\n \t\treturn ret;\n-\t}\n \n-\tpayload->data.resize(hdr.data);\n-\tpayload->fds.resize(hdr.fds);\n+\theaderReceived_ = false;\n+\tnotifier_->setEnabled(true);\n \n-\treturn recvData(payload->data.data(), hdr.data, payload->fds.data(), hdr.fds);\n+\treturn 0;\n }\n \n /**\n@@ -232,7 +234,8 @@ int IPCUnixSocket::receive(Payload *payload)\n  * \\brief A Signal emitted when a message is ready to be read\n  */\n \n-int IPCUnixSocket::sendData(const void *buffer, size_t length, const int32_t *fds, unsigned int num)\n+int IPCUnixSocket::sendData(const void *buffer, size_t length,\n+\t\t\t    const int32_t *fds, unsigned int num)\n {\n \tstruct iovec iov[1];\n \tiov[0].iov_base = const_cast<void *>(buffer);\n@@ -266,7 +269,8 @@ int IPCUnixSocket::sendData(const void *buffer, size_t length, const int32_t *fd\n \treturn 0;\n }\n \n-int IPCUnixSocket::recvData(void *buffer, size_t length, int32_t *fds, unsigned int num)\n+int IPCUnixSocket::recvData(void *buffer, size_t length,\n+\t\t\t    int32_t *fds, unsigned int num)\n {\n \tstruct iovec iov[1];\n \tiov[0].iov_base = buffer;\n@@ -291,8 +295,9 @@ int IPCUnixSocket::recvData(void *buffer, size_t length, int32_t *fds, unsigned\n \n \tif (recvmsg(fd_, &msg, 0) < 0) {\n \t\tint ret = -errno;\n-\t\tLOG(IPCUnixSocket, Error)\n-\t\t\t<< \"Failed to recvmsg: \" << strerror(-ret);\n+\t\tif (ret != -EAGAIN)\n+\t\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t\t<< \"Failed to recvmsg: \" << strerror(-ret);\n \t\treturn ret;\n \t}\n \n@@ -303,6 +308,35 @@ int IPCUnixSocket::recvData(void *buffer, size_t length, int32_t *fds, unsigned\n \n void IPCUnixSocket::dataNotifier(EventNotifier *notifier)\n {\n+\tint ret;\n+\n+\tif (!headerReceived_) {\n+\t\t/* Receive the header. */\n+\t\tret = ::recv(fd_, &header_, sizeof(header_), 0);\n+\t\tif (ret < 0) {\n+\t\t\tret = -errno;\n+\t\t\tLOG(IPCUnixSocket, Error)\n+\t\t\t\t<< \"Failed to receive header: \" << strerror(-ret);\n+\t\t\treturn;\n+\t\t}\n+\n+\t\theaderReceived_ = true;\n+\t}\n+\n+\t/*\n+\t * If the payload has arrived, disable the notifier and emit the\n+\t * readyRead signal. The notifier will be reenabled by the receive()\n+\t * method.\n+\t */\n+\tstruct pollfd fds = { fd_, POLLIN, 0 };\n+\tret = poll(&fds, 1, 0);\n+\tif (ret < 0)\n+\t\treturn;\n+\n+\tif (!(fds.revents & POLLIN))\n+\t\treturn;\n+\n+\tnotifier_->setEnabled(false);\n \treadyRead.emit(this);\n }\n \n","prefixes":["libcamera-devel","v4","3/3"]}