Show a patch.

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

{
    "id": 12270,
    "url": "https://patchwork.libcamera.org/api/patches/12270/?format=api",
    "web_url": "https://patchwork.libcamera.org/patch/12270/",
    "project": {
        "id": 1,
        "url": "https://patchwork.libcamera.org/api/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": "<20210513092246.42847-9-jacopo@jmondi.org>",
    "date": "2021-05-13T09:22:46",
    "name": "[libcamera-devel,v2,8/8] android: Implement flush() camera operation",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": false,
    "hash": "4c02407b675fd1d76edb75d5f490f0c4d8c2d18d",
    "submitter": {
        "id": 3,
        "url": "https://patchwork.libcamera.org/api/people/3/?format=api",
        "name": "Jacopo Mondi",
        "email": "jacopo@jmondi.org"
    },
    "delegate": {
        "id": 15,
        "url": "https://patchwork.libcamera.org/api/users/15/?format=api",
        "username": "jmondi",
        "first_name": "Jacopo",
        "last_name": "Mondi",
        "email": "jacopo@jmondi.org"
    },
    "mbox": "https://patchwork.libcamera.org/patch/12270/mbox/",
    "series": [
        {
            "id": 2025,
            "url": "https://patchwork.libcamera.org/api/series/2025/?format=api",
            "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=2025",
            "date": "2021-05-13T09:22:38",
            "name": "Implement flush() camera operation",
            "version": 2,
            "mbox": "https://patchwork.libcamera.org/series/2025/mbox/"
        }
    ],
    "comments": "https://patchwork.libcamera.org/api/patches/12270/comments/",
    "check": "pending",
    "checks": "https://patchwork.libcamera.org/api/patches/12270/checks/",
    "tags": {},
    "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 404C4C31EF\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 13 May 2021 09:22:16 +0000 (UTC)",
            "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9A97968933;\n\tThu, 13 May 2021 11:22:15 +0200 (CEST)",
            "from relay1-d.mail.gandi.net (relay1-d.mail.gandi.net\n\t[217.70.183.193])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B253168921\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 13 May 2021 11:22:12 +0200 (CEST)",
            "from uno.LocalDomain (93-61-96-190.ip145.fastwebnet.it\n\t[93.61.96.190]) (Authenticated sender: jacopo@jmondi.org)\n\tby relay1-d.mail.gandi.net (Postfix) with ESMTPSA id 47ECA240007;\n\tThu, 13 May 2021 09:22:12 +0000 (UTC)"
        ],
        "From": "Jacopo Mondi <jacopo@jmondi.org>",
        "To": "libcamera-devel@lists.libcamera.org",
        "Date": "Thu, 13 May 2021 11:22:46 +0200",
        "Message-Id": "<20210513092246.42847-9-jacopo@jmondi.org>",
        "X-Mailer": "git-send-email 2.31.1",
        "In-Reply-To": "<20210513092246.42847-1-jacopo@jmondi.org>",
        "References": "<20210513092246.42847-1-jacopo@jmondi.org>",
        "MIME-Version": "1.0",
        "Subject": "[libcamera-devel] [PATCH v2 8/8] android: Implement flush() camera\n\toperation",
        "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>",
        "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>"
    },
    "content": "Implement the flush() camera operation in the CameraDevice class\nand make it available to the camera framework by implementing the\noperation wrapper in camera_ops.cpp.\n\nThe flush() implementation stops the Camera and the worker thread and\nwaits for all in-flight requests to be returned. Stopping the Camera\nforces all Requests already queued to be returned immediately in error\nstate. As flush() has to wait until all of them have been returned, make it\nwait on a newly introduced condition variable which is notified by the\nrequest completion handler when the queue of pending requests has been\nexhausted.\n\nAs flush() can race with processCaptureRequest() protect the requests\nqueueing by introducing a new CameraState::CameraFlushing state that\nprocessCaptureRequest() inspects before queuing the Request to the\nCamera. If flush() has been called while processCaptureRequest() was\nexecuting, return the current Request immediately in error state.\n\nSigned-off-by: Jacopo Mondi <jacopo@jmondi.org>\n---\n src/android/camera_device.cpp | 91 +++++++++++++++++++++++++++++++++--\n src/android/camera_device.h   |  9 +++-\n src/android/camera_ops.cpp    |  8 ++-\n 3 files changed, 102 insertions(+), 6 deletions(-)",
    "diff": "diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\nindex 7f8c9bd7832d..6d6730a50ec7 100644\n--- a/src/android/camera_device.cpp\n+++ b/src/android/camera_device.cpp\n@@ -750,16 +750,64 @@ int CameraDevice::open(const hw_module_t *hardwareModule)\n \n void CameraDevice::close()\n {\n-\tstreams_.clear();\n+\tMutexLocker cameraLock(cameraMutex_);\n+\tif (state_ == CameraFlushing) {\n+\t\tMutexLocker flushLock(flushMutex_);\n+\t\tflushed_.wait(flushLock, [&] { return state_ != CameraStopped; });\n+\t\tcamera_->release();\n \n+\t\treturn;\n+\t}\n+\n+\tstreams_.clear();\n \tstop();\n \n \tcamera_->release();\n }\n \n+/*\n+ * Flush is similar to stop() but sets the camera state to 'flushing' and wait\n+ * until all the in-flight requests have been returned before setting the\n+ * camera state to stopped.\n+ *\n+ * Once flushing is done it unlocks concurrent camera close() calls or new\n+ * camera configurations.\n+ */\n+void CameraDevice::flush()\n+{\n+\t{\n+\t\tMutexLocker cameraLock(cameraMutex_);\n+\n+\t\tif (state_ != CameraRunning)\n+\t\t\treturn;\n+\n+\t\tworker_.stop();\n+\t\tcamera_->stop();\n+\t\tstate_ = CameraFlushing;\n+\t}\n+\n+\t/*\n+\t * Now wait for all the in-flight requests to be completed before\n+\t * continuing. Stopping the Camera guarantees that all in-flight\n+\t * requests are completed in error state.\n+\t */\n+\t{\n+\t\tMutexLocker requestsLock(requestsMutex_);\n+\t\tflushing_.wait(requestsLock, [&] { return descriptors_.empty(); });\n+\t}\n+\n+\t/*\n+\t * Unlock close() or configureStreams() that might be waiting for\n+\t * flush to be completed.\n+\t */\n+\tMutexLocker flushLocker(flushMutex_);\n+\tstate_ = CameraStopped;\n+\tflushed_.notify_one();\n+}\n+\n+/* Calls to stop() must be protected by cameraMutex_ being held by the caller. */\n void CameraDevice::stop()\n {\n-\tMutexLocker cameraLock(cameraMutex_);\n \tif (state_ == CameraStopped)\n \t\treturn;\n \n@@ -1652,8 +1700,20 @@ PixelFormat CameraDevice::toPixelFormat(int format) const\n  */\n int CameraDevice::configureStreams(camera3_stream_configuration_t *stream_list)\n {\n-\t/* Before any configuration attempt, stop the camera. */\n-\tstop();\n+\t{\n+\t\t/*\n+\t\t * If a flush is in progress, wait for it to complete and to\n+\t\t * stop the camera, otherwise before any new configuration\n+\t\t * attempt we have to stop the camera explictely.\n+\t\t */\n+\t\tMutexLocker cameraLock(cameraMutex_);\n+\t\tif (state_ == CameraFlushing) {\n+\t\t\tMutexLocker flushLock(flushMutex_);\n+\t\t\tflushed_.wait(flushLock, [&] { return state_ != CameraStopped; });\n+\t\t} else {\n+\t\t\tstop();\n+\t\t}\n+\t}\n \n \tif (stream_list->num_streams == 0) {\n \t\tLOG(HAL, Error) << \"No streams in configuration\";\n@@ -2021,6 +2081,25 @@ int CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques\n \tif (ret)\n \t\treturn ret;\n \n+\t/*\n+\t * Just before queuing the request, make sure flush() has not\n+\t * been called after this function has been executed. In that\n+\t * case, immediately return the request with errors.\n+\t */\n+\tMutexLocker cameraLock(cameraMutex_);\n+\tif (state_ == CameraFlushing || state_ == CameraStopped) {\n+\t\tfor (camera3_stream_buffer_t &buffer : descriptor.buffers_) {\n+\t\t\tbuffer.status = CAMERA3_BUFFER_STATUS_ERROR;\n+\t\t\tbuffer.release_fence = buffer.acquire_fence;\n+\t\t}\n+\n+\t\tnotifyError(descriptor.frameNumber_,\n+\t\t\t    descriptor.buffers_[0].stream,\n+\t\t\t    CAMERA3_MSG_ERROR_REQUEST);\n+\n+\t\treturn 0;\n+\t}\n+\n \tworker_.queueRequest(descriptor.request_.get());\n \n \t{\n@@ -2050,6 +2129,10 @@ void CameraDevice::requestComplete(Request *request)\n \t\t\treturn;\n \t\t}\n \n+\t\t/* Release flush if all the pending requests have been completed. */\n+\t\tif (descriptors_.empty())\n+\t\t\tflushing_.notify_one();\n+\n \t\tnode = descriptors_.extract(it);\n \t}\n \tCamera3RequestDescriptor &descriptor = node.mapped();\ndiff --git a/src/android/camera_device.h b/src/android/camera_device.h\nindex ed992ae56d5d..9c55cc2674b7 100644\n--- a/src/android/camera_device.h\n+++ b/src/android/camera_device.h\n@@ -7,6 +7,7 @@\n #ifndef __ANDROID_CAMERA_DEVICE_H__\n #define __ANDROID_CAMERA_DEVICE_H__\n \n+#include <condition_variable>\n #include <map>\n #include <memory>\n #include <mutex>\n@@ -42,6 +43,7 @@ public:\n \n \tint open(const hw_module_t *hardwareModule);\n \tvoid close();\n+\tvoid flush();\n \n \tunsigned int id() const { return id_; }\n \tcamera3_device_t *camera3Device() { return &camera3Device_; }\n@@ -92,6 +94,7 @@ private:\n \tenum State {\n \t\tCameraStopped,\n \t\tCameraRunning,\n+\t\tCameraFlushing,\n \t};\n \n \tvoid stop();\n@@ -124,6 +127,9 @@ private:\n \tlibcamera::Mutex cameraMutex_; /* Protects access to the camera state. */\n \tState state_;\n \n+\tlibcamera::Mutex flushMutex_; /* Protects the flushed_ condition variable. */\n+\tstd::condition_variable flushed_;\n+\n \tstd::shared_ptr<libcamera::Camera> camera_;\n \tstd::unique_ptr<libcamera::CameraConfiguration> config_;\n \n@@ -135,8 +141,9 @@ private:\n \tstd::map<int, libcamera::PixelFormat> formatsMap_;\n \tstd::vector<CameraStream> streams_;\n \n-\tlibcamera::Mutex requestsMutex_; /* Protects descriptors_. */\n+\tlibcamera::Mutex requestsMutex_; /* Protects descriptors_ and flushing_. */\n \tstd::map<uint64_t, Camera3RequestDescriptor> descriptors_;\n+\tstd::condition_variable flushing_;\n \n \tstd::string maker_;\n \tstd::string model_;\ndiff --git a/src/android/camera_ops.cpp b/src/android/camera_ops.cpp\nindex 696e80436821..8a3cfa175ff5 100644\n--- a/src/android/camera_ops.cpp\n+++ b/src/android/camera_ops.cpp\n@@ -66,8 +66,14 @@ static void hal_dev_dump([[maybe_unused]] const struct camera3_device *dev,\n {\n }\n \n-static int hal_dev_flush([[maybe_unused]] const struct camera3_device *dev)\n+static int hal_dev_flush(const struct camera3_device *dev)\n {\n+\tif (!dev)\n+\t\treturn -EINVAL;\n+\n+\tCameraDevice *camera = reinterpret_cast<CameraDevice *>(dev->priv);\n+\tcamera->flush();\n+\n \treturn 0;\n }\n \n",
    "prefixes": [
        "libcamera-devel",
        "v2",
        "8/8"
    ]
}