[{"id":21326,"web_url":"https://patchwork.libcamera.org/comment/21326/","msgid":"<CAO5uPHNgvKxamPx32V-NCFhPBS_0qpBes=4WJovqPA93grExcA@mail.gmail.com>","date":"2021-11-29T14:04:26","subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","submitter":{"id":63,"url":"https://patchwork.libcamera.org/api/people/63/","name":"Hirokazu Honda","email":"hiroh@chromium.org"},"content":"Hi Laurent,\n\nOn Mon, Nov 29, 2021 at 8:58 AM Laurent Pinchart\n<laurent.pinchart@ideasonboard.com> wrote:\n>\n> Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.\n> Rename it to SharedFD.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nReviewed-by: Hirokazu Honda <hiroh@chromium.org>\n> ---\n>  include/libcamera/base/file.h                 |   4 +-\n>  include/libcamera/base/meson.build            |   2 +-\n>  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-\n>  include/libcamera/framebuffer.h               |   4 +-\n>  .../libcamera/internal/ipa_data_serializer.h  |  40 +--\n>  include/libcamera/internal/ipc_pipe.h         |   8 +-\n>  include/libcamera/ipa/core.mojom              |   6 +-\n>  include/libcamera/ipa/raspberrypi.mojom       |   2 +-\n>  src/android/camera_device.cpp                 |   2 +-\n>  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-\n>  src/libcamera/base/file.cpp                   |   6 +-\n>  src/libcamera/base/file_descriptor.cpp        | 266 ------------------\n>  src/libcamera/base/meson.build                |   2 +-\n>  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++\n>  src/libcamera/framebuffer.cpp                 |   6 +-\n>  src/libcamera/ipa_data_serializer.cpp         | 100 +++----\n>  src/libcamera/ipc_pipe.cpp                    |   4 +-\n>  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-\n>  src/libcamera/v4l2_videodevice.cpp            |   6 +-\n>  src/v4l2/v4l2_camera.h                        |   2 +-\n>  test/meson.build                              |   2 +-\n>  .../ipa_data_serializer_test.cpp              |  14 +-\n>  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--\n>  .../module_ipa_proxy.cpp.tmpl                 |   2 +-\n>  .../module_ipa_proxy.h.tmpl                   |   2 +-\n>  .../libcamera_templates/proxy_functions.tmpl  |   2 +-\n>  .../libcamera_templates/serializer.tmpl       |  22 +-\n>  .../generators/mojom_libcamera_generator.py   |   6 +-\n>  28 files changed, 422 insertions(+), 426 deletions(-)\n>  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)\n>  delete mode 100644 src/libcamera/base/file_descriptor.cpp\n>  create mode 100644 src/libcamera/base/shared_fd.cpp\n>  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)\n>\n> diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h\n> index 47769da7abc2..691b52d6ab2d 100644\n> --- a/include/libcamera/base/file.h\n> +++ b/include/libcamera/base/file.h\n> @@ -21,7 +21,7 @@\n>\n>  namespace libcamera {\n>\n> -class FileDescriptor;\n> +class SharedFD;\n>\n>  class File\n>  {\n> @@ -69,7 +69,7 @@ public:\n>         bool unmap(uint8_t *addr);\n>\n>         static bool exists(const std::string &name);\n> -       static ino_t inode(const FileDescriptor &fd);\n> +       static ino_t inode(const SharedFD &fd);\n>\n>  private:\n>         LIBCAMERA_DISABLE_COPY(File)\n> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\n> index cca374a769cc..112420dab225 100644\n> --- a/include/libcamera/base/meson.build\n> +++ b/include/libcamera/base/meson.build\n> @@ -11,13 +11,13 @@ libcamera_base_headers = files([\n>      'event_dispatcher_poll.h',\n>      'event_notifier.h',\n>      'file.h',\n> -    'file_descriptor.h',\n>      'flags.h',\n>      'log.h',\n>      'message.h',\n>      'object.h',\n>      'private.h',\n>      'semaphore.h',\n> +    'shared_fd.h',\n>      'signal.h',\n>      'span.h',\n>      'thread.h',\n> diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h\n> similarity index 55%\n> rename from include/libcamera/base/file_descriptor.h\n> rename to include/libcamera/base/shared_fd.h\n> index 12a43f95d414..a786885ceb32 100644\n> --- a/include/libcamera/base/file_descriptor.h\n> +++ b/include/libcamera/base/shared_fd.h\n> @@ -2,7 +2,7 @@\n>  /*\n>   * Copyright (C) 2019, Google Inc.\n>   *\n> - * file_descriptor.h - File descriptor wrapper\n> + * shared_fd.h - File descriptor wrapper with shared ownership\n>   */\n>\n>  #pragma once\n> @@ -13,18 +13,18 @@ namespace libcamera {\n>\n>  class UniqueFD;\n>\n> -class FileDescriptor final\n> +class SharedFD final\n>  {\n>  public:\n> -       explicit FileDescriptor(const int &fd = -1);\n> -       explicit FileDescriptor(int &&fd);\n> -       explicit FileDescriptor(UniqueFD fd);\n> -       FileDescriptor(const FileDescriptor &other);\n> -       FileDescriptor(FileDescriptor &&other);\n> -       ~FileDescriptor();\n> +       explicit SharedFD(const int &fd = -1);\n> +       explicit SharedFD(int &&fd);\n> +       explicit SharedFD(UniqueFD fd);\n> +       SharedFD(const SharedFD &other);\n> +       SharedFD(SharedFD &&other);\n> +       ~SharedFD();\n>\n> -       FileDescriptor &operator=(const FileDescriptor &other);\n> -       FileDescriptor &operator=(FileDescriptor &&other);\n> +       SharedFD &operator=(const SharedFD &other);\n> +       SharedFD &operator=(SharedFD &&other);\n>\n>         bool isValid() const { return fd_ != nullptr; }\n>         int fd() const { return fd_ ? fd_->fd() : -1; }\n> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> index 2fbea9c5be16..357bbe189551 100644\n> --- a/include/libcamera/framebuffer.h\n> +++ b/include/libcamera/framebuffer.h\n> @@ -13,7 +13,7 @@\n>  #include <vector>\n>\n>  #include <libcamera/base/class.h>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/span.h>\n>\n>  namespace libcamera {\n> @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible\n>  public:\n>         struct Plane {\n>                 static constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();\n> -               FileDescriptor fd;\n> +               SharedFD fd;\n>                 unsigned int offset = kInvalidOffset;\n>                 unsigned int length;\n>         };\n> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\n> index c2f602d5b7de..a87449c9be48 100644\n> --- a/include/libcamera/internal/ipa_data_serializer.h\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -66,7 +66,7 @@ template<typename T>\n>  class IPADataSerializer\n>  {\n>  public:\n> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>         serialize(const T &data, ControlSerializer *cs = nullptr);\n>\n>         static T deserialize(const std::vector<uint8_t> &data,\n> @@ -76,12 +76,12 @@ public:\n>                              ControlSerializer *cs = nullptr);\n>\n>         static T deserialize(const std::vector<uint8_t> &data,\n> -                            const std::vector<FileDescriptor> &fds,\n> +                            const std::vector<SharedFD> &fds,\n>                              ControlSerializer *cs = nullptr);\n>         static T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                              std::vector<uint8_t>::const_iterator dataEnd,\n> -                            std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                            std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                            std::vector<SharedFD>::const_iterator fdsBegin,\n> +                            std::vector<SharedFD>::const_iterator fdsEnd,\n>                              ControlSerializer *cs = nullptr);\n>  };\n>\n> @@ -104,11 +104,11 @@ template<typename V>\n>  class IPADataSerializer<std::vector<V>>\n>  {\n>  public:\n> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>         serialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n>         {\n>                 std::vector<uint8_t> dataVec;\n> -               std::vector<FileDescriptor> fdsVec;\n> +               std::vector<SharedFD> fdsVec;\n>\n>                 /* Serialize the length. */\n>                 uint32_t vecLen = data.size();\n> @@ -117,7 +117,7 @@ public:\n>                 /* Serialize the members. */\n>                 for (auto const &it : data) {\n>                         std::vector<uint8_t> dvec;\n> -                       std::vector<FileDescriptor> fvec;\n> +                       std::vector<SharedFD> fvec;\n>\n>                         std::tie(dvec, fvec) =\n>                                 IPADataSerializer<V>::serialize(it, cs);\n> @@ -141,11 +141,11 @@ public:\n>                                           std::vector<uint8_t>::const_iterator dataEnd,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n> -               std::vector<FileDescriptor> fds;\n> +               std::vector<SharedFD> fds;\n>                 return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n>         }\n>\n> -       static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> +       static std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n>                 return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> @@ -153,15 +153,15 @@ public:\n>\n>         static std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                           std::vector<uint8_t>::const_iterator dataEnd,\n> -                                         std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                         std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n>                 uint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n>                 std::vector<V> ret(vecLen);\n>\n>                 std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> -               std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> +               std::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n>                 for (uint32_t i = 0; i < vecLen; i++) {\n>                         uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n>                         uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> @@ -201,11 +201,11 @@ template<typename K, typename V>\n>  class IPADataSerializer<std::map<K, V>>\n>  {\n>  public:\n> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>         serialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n>         {\n>                 std::vector<uint8_t> dataVec;\n> -               std::vector<FileDescriptor> fdsVec;\n> +               std::vector<SharedFD> fdsVec;\n>\n>                 /* Serialize the length. */\n>                 uint32_t mapLen = data.size();\n> @@ -214,7 +214,7 @@ public:\n>                 /* Serialize the members. */\n>                 for (auto const &it : data) {\n>                         std::vector<uint8_t> dvec;\n> -                       std::vector<FileDescriptor> fvec;\n> +                       std::vector<SharedFD> fvec;\n>\n>                         std::tie(dvec, fvec) =\n>                                 IPADataSerializer<K>::serialize(it.first, cs);\n> @@ -247,11 +247,11 @@ public:\n>                                           std::vector<uint8_t>::const_iterator dataEnd,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n> -               std::vector<FileDescriptor> fds;\n> +               std::vector<SharedFD> fds;\n>                 return deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n>         }\n>\n> -       static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> +       static std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n>                 return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> @@ -259,8 +259,8 @@ public:\n>\n>         static std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                           std::vector<uint8_t>::const_iterator dataEnd,\n> -                                         std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                         std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                           ControlSerializer *cs = nullptr)\n>         {\n>                 std::map<K, V> ret;\n> @@ -268,7 +268,7 @@ public:\n>                 uint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n>\n>                 std::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> -               std::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> +               std::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n>                 for (uint32_t i = 0; i < mapLen; i++) {\n>                         uint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n>                         uint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h\n> index 986f8d886fa6..ab5dd67c3813 100644\n> --- a/include/libcamera/internal/ipc_pipe.h\n> +++ b/include/libcamera/internal/ipc_pipe.h\n> @@ -9,7 +9,7 @@\n>\n>  #include <vector>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/signal.h>\n>\n>  #include \"libcamera/internal/ipc_unixsocket.h\"\n> @@ -33,17 +33,17 @@ public:\n>\n>         Header &header() { return header_; }\n>         std::vector<uint8_t> &data() { return data_; }\n> -       std::vector<FileDescriptor> &fds() { return fds_; }\n> +       std::vector<SharedFD> &fds() { return fds_; }\n>\n>         const Header &header() const { return header_; }\n>         const std::vector<uint8_t> &data() const { return data_; }\n> -       const std::vector<FileDescriptor> &fds() const { return fds_; }\n> +       const std::vector<SharedFD> &fds() const { return fds_; }\n>\n>  private:\n>         Header header_;\n>\n>         std::vector<uint8_t> data_;\n> -       std::vector<FileDescriptor> fds_;\n> +       std::vector<SharedFD> fds_;\n>  };\n>\n>  class IPCPipe\n> diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom\n> index f7eff0c7ab8c..74f3339e56f2 100644\n> --- a/include/libcamera/ipa/core.mojom\n> +++ b/include/libcamera/ipa/core.mojom\n> @@ -32,7 +32,7 @@ module libcamera;\n>   *   - This attribute instructs the build system that a (de)serializer is\n>   *     available for the type and there's no need to generate one\n>   * - hasFd - struct fields or empty structs only\n> - *   - Designate that this field or empty struct contains a FileDescriptor\n> + *   - Designate that this field or empty struct contains a SharedFD\n>   *\n>   * Rules:\n>   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is\n> @@ -60,7 +60,7 @@ module libcamera;\n>   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array\n>   *       member\n>   * - [skipHeader] and [skipSerdes] only work here in core.mojom\n> - * - If a field in a struct has a FileDescriptor, but is not explicitly\n> + * - If a field in a struct has a SharedFD, but is not explicitly\n>   *   defined so in mojom, then the field must be marked with the [hasFd]\n>   *   attribute\n>   *\n> @@ -71,7 +71,7 @@ module libcamera;\n>   */\n>  [skipSerdes, skipHeader] struct ControlInfoMap {};\n>  [skipSerdes, skipHeader] struct ControlList {};\n> -[skipSerdes, skipHeader] struct FileDescriptor {};\n> +[skipSerdes, skipHeader] struct SharedFD {};\n>\n>  [skipHeader] struct Point {\n>         int32 x;\n> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> index e453d46cb14f..acd3cafe6c91 100644\n> --- a/include/libcamera/ipa/raspberrypi.mojom\n> +++ b/include/libcamera/ipa/raspberrypi.mojom\n> @@ -35,7 +35,7 @@ struct ISPConfig {\n>\n>  struct IPAConfig {\n>         uint32 transform;\n> -       libcamera.FileDescriptor lsTableHandle;\n> +       libcamera.SharedFD lsTableHandle;\n>  };\n>\n>  struct StartConfig {\n> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\n> index f2e0bdbdbbf6..1938b10509fa 100644\n> --- a/src/android/camera_device.cpp\n> +++ b/src/android/camera_device.cpp\n> @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n>\n>         std::vector<FrameBuffer::Plane> planes(buf.numPlanes());\n>         for (size_t i = 0; i < buf.numPlanes(); ++i) {\n> -               FileDescriptor fd{ camera3buffer->data[i] };\n> +               SharedFD fd{ camera3buffer->data[i] };\n>                 if (!fd.isValid()) {\n>                         LOG(HAL, Fatal) << \"No valid fd\";\n>                         return nullptr;\n> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> index c6aec09046f7..aaf629eeb3fc 100644\n> --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> @@ -15,8 +15,8 @@\n>\n>  #include <linux/bcm2835-isp.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/span.h>\n>\n>  #include <libcamera/control_ids.h>\n> @@ -164,7 +164,7 @@ private:\n>         bool processPending_;\n>\n>         /* LS table allocation passed in from the pipeline handler. */\n> -       FileDescriptor lsTableHandle_;\n> +       SharedFD lsTableHandle_;\n>         void *lsTable_;\n>\n>         /* Distinguish the first camera start from others. */\n> diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp\n> index 66c73c406198..3ca9839bb989 100644\n> --- a/src/libcamera/base/file.cpp\n> +++ b/src/libcamera/base/file.cpp\n> @@ -14,8 +14,8 @@\n>  #include <sys/types.h>\n>  #include <unistd.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  /**\n>   * \\file base/file.h\n> @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)\n>  }\n>\n>  /**\n> - * \\brief Retrieve the inode of a FileDescriptor\n> + * \\brief Retrieve the inode of a SharedFD\n>   *\n>   * \\return The file descriptor inode on success, or 0 on error\n>   */\n> -ino_t File::inode(const FileDescriptor &fd)\n> +ino_t File::inode(const SharedFD &fd)\n>  {\n>         if (!fd.isValid())\n>                 return 0;\n> diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp\n> deleted file mode 100644\n> index a83bf52c31e6..000000000000\n> --- a/src/libcamera/base/file_descriptor.cpp\n> +++ /dev/null\n> @@ -1,266 +0,0 @@\n> -/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> -/*\n> - * Copyright (C) 2019, Google Inc.\n> - *\n> - * file_descriptor.cpp - File descriptor wrapper\n> - */\n> -\n> -#include <libcamera/base/file_descriptor.h>\n> -\n> -#include <string.h>\n> -#include <sys/types.h>\n> -#include <unistd.h>\n> -#include <utility>\n> -\n> -#include <libcamera/base/log.h>\n> -#include <libcamera/base/unique_fd.h>\n> -\n> -/**\n> - * \\file base/file_descriptor.h\n> - * \\brief File descriptor wrapper\n> - */\n> -\n> -namespace libcamera {\n> -\n> -LOG_DEFINE_CATEGORY(FileDescriptor)\n> -\n> -/**\n> - * \\class FileDescriptor\n> - * \\brief RAII-style wrapper for file descriptors\n> - *\n> - * The FileDescriptor class provides RAII-style lifetime management of file\n> - * descriptors with an efficient mechanism for ownership sharing. At its core,\n> - * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> - * integer) with an RAII-style interface. The Descriptor is then implicitly\n> - * shared with all FileDescriptor instances constructed as copies.\n> - *\n> - * When constructed from a numerical file descriptor, the FileDescriptor\n> - * instance either duplicates or takes over the file descriptor:\n> - *\n> - * - The FileDescriptor(const int &) constructor duplicates the numerical file\n> - *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> - *   responsible for closing the original file descriptor, and the value\n> - *   returned by fd() will be different from the value passed to the\n> - *   constructor.\n> - *\n> - * - The FileDescriptor(int &&) constructor takes over the numerical file\n> - *   descriptor and wraps it in a Descriptor. The caller shall not touch the\n> - *   original file descriptor once the function returns, and the value returned\n> - *   by fd() will be identical to the value passed to the constructor.\n> - *\n> - * The copy constructor and assignment operator create copies that share the\n> - * Descriptor, while the move versions of those functions additionally make the\n> - * other FileDescriptor invalid. When the last FileDescriptor that references a\n> - * Descriptor is destroyed, the file descriptor is closed.\n> - *\n> - * The numerical file descriptor is available through the fd() function. All\n> - * FileDescriptor instances created as copies of a FileDescriptor will report\n> - * the same fd() value. Callers can perform operations on the fd(), but shall\n> - * never close it manually.\n> - */\n> -\n> -/**\n> - * \\brief Create a FileDescriptor copying a given \\a fd\n> - * \\param[in] fd File descriptor\n> - *\n> - * Construct a FileDescriptor from a numerical file descriptor by duplicating\n> - * the \\a fd, and take ownership of the copy. The original \\a fd is left\n> - * untouched, and the caller is responsible for closing it when appropriate.\n> - * The duplicated file descriptor will be closed automatically when all\n> - * FileDescriptor instances that reference it are destroyed.\n> - *\n> - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> - * the fd() function will return -1.\n> - */\n> -FileDescriptor::FileDescriptor(const int &fd)\n> -{\n> -       if (fd < 0)\n> -               return;\n> -\n> -       fd_ = std::make_shared<Descriptor>(fd, true);\n> -       if (fd_->fd() < 0)\n> -               fd_.reset();\n> -}\n> -\n> -/**\n> - * \\brief Create a FileDescriptor taking ownership of a given \\a fd\n> - * \\param[in] fd File descriptor\n> - *\n> - * Construct a FileDescriptor from a numerical file descriptor by taking\n> - * ownership of the \\a fd. The original \\a fd is set to -1 and shall not be\n> - * touched by the caller anymore. In particular, the caller shall not close the\n> - * original \\a fd manually. The duplicated file descriptor will be closed\n> - * automatically when all FileDescriptor instances that reference it are\n> - * destroyed.\n> - *\n> - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> - * the fd() function will return -1.\n> - */\n> -FileDescriptor::FileDescriptor(int &&fd)\n> -{\n> -       if (fd < 0)\n> -               return;\n> -\n> -       fd_ = std::make_shared<Descriptor>(fd, false);\n> -       /*\n> -        * The Descriptor constructor can't have failed here, as it took over\n> -        * the fd without duplicating it. Just set the original fd to -1 to\n> -        * implement move semantics.\n> -        */\n> -       fd = -1;\n> -}\n> -\n> -/**\n> - * \\brief Create a FileDescriptor taking ownership of a given UniqueFD \\a fd\n> - * \\param[in] fd UniqueFD\n> - *\n> - * Construct a FileDescriptor from UniqueFD by taking ownership of the \\a fd.\n> - * The original \\a fd becomes invalid.\n> - */\n> -FileDescriptor::FileDescriptor(UniqueFD fd)\n> -       : FileDescriptor(fd.release())\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Copy constructor, create a FileDescriptor from a copy of \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Copying a FileDescriptor implicitly shares ownership of the wrapped file\n> - * descriptor. The original FileDescriptor is left untouched, and the caller is\n> - * responsible for destroying it when appropriate. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - */\n> -FileDescriptor::FileDescriptor(const FileDescriptor &other)\n> -       : fd_(other.fd_)\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Move constructor, create a FileDescriptor by taking over \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> - * by \\a other to the new FileDescriptor. The \\a other FileDescriptor is\n> - * invalidated and its fd() function will return -1. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - */\n> -FileDescriptor::FileDescriptor(FileDescriptor &&other)\n> -       : fd_(std::move(other.fd_))\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Destroy the FileDescriptor instance\n> - *\n> - * Destroying a FileDescriptor instance releases its reference to the wrapped\n> - * descriptor, if any. When the last instance that references a wrapped\n> - * descriptor is destroyed, the file descriptor is automatically closed.\n> - */\n> -FileDescriptor::~FileDescriptor()\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> - * copy of \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Copying a FileDescriptor creates a new reference to the wrapped file\n> - * descriptor owner by \\a other. If \\a other is invalid, *this will also be\n> - * invalid. The original FileDescriptor is left untouched, and the caller is\n> - * responsible for destroying it when appropriate. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - *\n> - * \\return A reference to this FileDescriptor\n> - */\n> -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)\n> -{\n> -       fd_ = other.fd_;\n> -\n> -       return *this;\n> -}\n> -\n> -/**\n> - * \\brief Move assignment operator, replace the wrapped file descriptor by\n> - * taking over \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> - * by \\a other to the new FileDescriptor. If \\a other is invalid, *this will\n> - * also be invalid. The \\a other FileDescriptor is invalidated and its fd()\n> - * function will return -1. The wrapped file descriptor will be closed\n> - * automatically when all FileDescriptor instances that reference it are\n> - * destroyed.\n> - *\n> - * \\return A reference to this FileDescriptor\n> - */\n> -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)\n> -{\n> -       fd_ = std::move(other.fd_);\n> -\n> -       return *this;\n> -}\n> -\n> -/**\n> - * \\fn FileDescriptor::isValid()\n> - * \\brief Check if the FileDescriptor instance is valid\n> - * \\return True if the FileDescriptor is valid, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn FileDescriptor::fd()\n> - * \\brief Retrieve the numerical file descriptor\n> - * \\return The numerical file descriptor, which may be -1 if the FileDescriptor\n> - * instance is invalid\n> - */\n> -\n> -/**\n> - * \\brief Duplicate a FileDescriptor\n> - *\n> - * Duplicating a FileDescriptor creates a duplicate of the wrapped file\n> - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function\n> - * of the original and the get() function of the duplicate will return different\n> - * values. The duplicate instance will not be affected by destruction of the\n> - * original instance or its copies.\n> - *\n> - * \\return A UniqueFD owning a duplicate of the original file descriptor\n> - */\n> -UniqueFD FileDescriptor::dup() const\n> -{\n> -       int dupFd = ::dup(fd());\n> -       if (dupFd == -1) {\n> -               int ret = -errno;\n> -               LOG(FileDescriptor, Error)\n> -                       << \"Failed to dup() fd: \" << strerror(-ret);\n> -       }\n> -\n> -       return UniqueFD(dupFd);\n> -}\n> -\n> -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)\n> -{\n> -       if (!duplicate) {\n> -               fd_ = fd;\n> -               return;\n> -       }\n> -\n> -       /* Failing to dup() a fd should not happen and is fatal. */\n> -       fd_ = ::dup(fd);\n> -       if (fd_ == -1) {\n> -               int ret = -errno;\n> -               LOG(FileDescriptor, Fatal)\n> -                       << \"Failed to dup() fd: \" << strerror(-ret);\n> -       }\n> -}\n> -\n> -FileDescriptor::Descriptor::~Descriptor()\n> -{\n> -       if (fd_ != -1)\n> -               close(fd_);\n> -}\n> -\n> -} /* namespace libcamera */\n> diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\n> index b0d85bc19245..ccb746c27466 100644\n> --- a/src/libcamera/base/meson.build\n> +++ b/src/libcamera/base/meson.build\n> @@ -8,12 +8,12 @@ libcamera_base_sources = files([\n>      'event_dispatcher_poll.cpp',\n>      'event_notifier.cpp',\n>      'file.cpp',\n> -    'file_descriptor.cpp',\n>      'flags.cpp',\n>      'log.cpp',\n>      'message.cpp',\n>      'object.cpp',\n>      'semaphore.cpp',\n> +    'shared_fd.cpp',\n>      'signal.cpp',\n>      'thread.cpp',\n>      'timer.cpp',\n> diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp\n> new file mode 100644\n> index 000000000000..05b6892f7e19\n> --- /dev/null\n> +++ b/src/libcamera/base/shared_fd.cpp\n> @@ -0,0 +1,262 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * shared_fd.cpp - File descriptor wrapper with shared ownership\n> + */\n> +\n> +#include <libcamera/base/shared_fd.h>\n> +\n> +#include <string.h>\n> +#include <sys/types.h>\n> +#include <unistd.h>\n> +#include <utility>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/base/unique_fd.h>\n> +\n> +/**\n> + * \\file base/shared_fd.h\n> + * \\brief File descriptor wrapper\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(SharedFD)\n> +\n> +/**\n> + * \\class SharedFD\n> + * \\brief RAII-style wrapper for file descriptors\n> + *\n> + * The SharedFD class provides RAII-style lifetime management of file\n> + * descriptors with an efficient mechanism for ownership sharing. At its core,\n> + * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> + * integer) with an RAII-style interface. The Descriptor is then implicitly\n> + * shared with all SharedFD instances constructed as copies.\n> + *\n> + * When constructed from a numerical file descriptor, the SharedFD instance\n> + * either duplicates or takes over the file descriptor:\n> + *\n> + * - The SharedFD(const int &) constructor duplicates the numerical file\n> + *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> + *   responsible for closing the original file descriptor, and the value\n> + *   returned by fd() will be different from the value passed to the\n> + *   constructor.\n> + *\n> + * - The SharedFD(int &&) constructor takes over the numerical file descriptor\n> + *   and wraps it in a Descriptor. The caller shall not touch the original file\n> + *   descriptor once the function returns, and the value returned by fd() will\n> + *   be identical to the value passed to the constructor.\n> + *\n> + * The copy constructor and assignment operator create copies that share the\n> + * Descriptor, while the move versions of those functions additionally make the\n> + * other SharedFD invalid. When the last SharedFD that references a Descriptor\n> + * is destroyed, the file descriptor is closed.\n> + *\n> + * The numerical file descriptor is available through the fd() function. All\n> + * SharedFD instances created as copies of a SharedFD will report the same fd()\n> + * value. Callers can perform operations on the fd(), but shall never close it\n> + * manually.\n> + */\n> +\n> +/**\n> + * \\brief Create a SharedFD copying a given \\a fd\n> + * \\param[in] fd File descriptor\n> + *\n> + * Construct a SharedFD from a numerical file descriptor by duplicating the\n> + * \\a fd, and take ownership of the copy. The original \\a fd is left untouched,\n> + * and the caller is responsible for closing it when appropriate. The duplicated\n> + * file descriptor will be closed automatically when all SharedFD instances that\n> + * reference it are destroyed.\n> + *\n> + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> + * function will return -1.\n> + */\n> +SharedFD::SharedFD(const int &fd)\n> +{\n> +       if (fd < 0)\n> +               return;\n> +\n> +       fd_ = std::make_shared<Descriptor>(fd, true);\n> +       if (fd_->fd() < 0)\n> +               fd_.reset();\n> +}\n> +\n> +/**\n> + * \\brief Create a SharedFD taking ownership of a given \\a fd\n> + * \\param[in] fd File descriptor\n> + *\n> + * Construct a SharedFD from a numerical file descriptor by taking ownership of\n> + * the \\a fd. The original \\a fd is set to -1 and shall not be touched by the\n> + * caller anymore. In particular, the caller shall not close the original \\a fd\n> + * manually. The duplicated file descriptor will be closed automatically when\n> + * all SharedFD instances that reference it are destroyed.\n> + *\n> + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> + * function will return -1.\n> + */\n> +SharedFD::SharedFD(int &&fd)\n> +{\n> +       if (fd < 0)\n> +               return;\n> +\n> +       fd_ = std::make_shared<Descriptor>(fd, false);\n> +       /*\n> +        * The Descriptor constructor can't have failed here, as it took over\n> +        * the fd without duplicating it. Just set the original fd to -1 to\n> +        * implement move semantics.\n> +        */\n> +       fd = -1;\n> +}\n> +\n> +/**\n> + * \\brief Create a SharedFD taking ownership of a given UniqueFD \\a fd\n> + * \\param[in] fd UniqueFD\n> + *\n> + * Construct a SharedFD from UniqueFD by taking ownership of the \\a fd. The\n> + * original \\a fd becomes invalid.\n> + */\n> +SharedFD::SharedFD(UniqueFD fd)\n> +       : SharedFD(fd.release())\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Copy constructor, create a SharedFD from a copy of \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Copying a SharedFD implicitly shares ownership of the wrapped file\n> + * descriptor. The original SharedFD is left untouched, and the caller is\n> + * responsible for destroying it when appropriate. The wrapped file descriptor\n> + * will be closed automatically when all SharedFD instances that reference it\n> + * are destroyed.\n> + */\n> +SharedFD::SharedFD(const SharedFD &other)\n> +       : fd_(other.fd_)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Move constructor, create a SharedFD by taking over \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> + * \\a other to the new SharedFD. The \\a other SharedFD is invalidated and its\n> + * fd() function will return -1. The wrapped file descriptor will be closed\n> + * automatically when all SharedFD instances that reference it are destroyed.\n> + */\n> +SharedFD::SharedFD(SharedFD &&other)\n> +       : fd_(std::move(other.fd_))\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Destroy the SharedFD instance\n> + *\n> + * Destroying a SharedFD instance releases its reference to the wrapped\n> + * descriptor, if any. When the last instance that references a wrapped\n> + * descriptor is destroyed, the file descriptor is automatically closed.\n> + */\n> +SharedFD::~SharedFD()\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> + * copy of \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Copying a SharedFD creates a new reference to the wrapped file descriptor\n> + * owner by \\a other. If \\a other is invalid, *this will also be invalid. The\n> + * original SharedFD is left untouched, and the caller is responsible for\n> + * destroying it when appropriate. The wrapped file descriptor will be closed\n> + * automatically when all SharedFD instances that reference it are destroyed.\n> + *\n> + * \\return A reference to this SharedFD\n> + */\n> +SharedFD &SharedFD::operator=(const SharedFD &other)\n> +{\n> +       fd_ = other.fd_;\n> +\n> +       return *this;\n> +}\n> +\n> +/**\n> + * \\brief Move assignment operator, replace the wrapped file descriptor by\n> + * taking over \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> + * \\a other to the new SharedFD. If \\a other is invalid, *this will also be\n> + * invalid. The \\a other SharedFD is invalidated and its fd() function will\n> + * return -1. The wrapped file descriptor will be closed automatically when\n> + * all SharedFD instances that reference it are destroyed.\n> + *\n> + * \\return A reference to this SharedFD\n> + */\n> +SharedFD &SharedFD::operator=(SharedFD &&other)\n> +{\n> +       fd_ = std::move(other.fd_);\n> +\n> +       return *this;\n> +}\n> +\n> +/**\n> + * \\fn SharedFD::isValid()\n> + * \\brief Check if the SharedFD instance is valid\n> + * \\return True if the SharedFD is valid, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn SharedFD::fd()\n> + * \\brief Retrieve the numerical file descriptor\n> + * \\return The numerical file descriptor, which may be -1 if the SharedFD\n> + * instance is invalid\n> + */\n> +\n> +/**\n> + * \\brief Duplicate a SharedFD\n> + *\n> + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and\n> + * returns a UniqueFD that owns the duplicate. The fd() function of the original\n> + * and the get() function of the duplicate will return different values. The\n> + * duplicate instance will not be affected by destruction of the original\n> + * instance or its copies.\n> + *\n> + * \\return A UniqueFD owning a duplicate of the original file descriptor\n> + */\n> +UniqueFD SharedFD::dup() const\n> +{\n> +       int dupFd = ::dup(fd());\n> +       if (dupFd == -1) {\n> +               int ret = -errno;\n> +               LOG(SharedFD, Error)\n> +                       << \"Failed to dup() fd: \" << strerror(-ret);\n> +       }\n> +\n> +       return UniqueFD(dupFd);\n> +}\n> +\n> +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)\n> +{\n> +       if (!duplicate) {\n> +               fd_ = fd;\n> +               return;\n> +       }\n> +\n> +       /* Failing to dup() a fd should not happen and is fatal. */\n> +       fd_ = ::dup(fd);\n> +       if (fd_ == -1) {\n> +               int ret = -errno;\n> +               LOG(SharedFD, Fatal)\n> +                       << \"Failed to dup() fd: \" << strerror(-ret);\n> +       }\n> +}\n> +\n> +SharedFD::Descriptor::~Descriptor()\n> +{\n> +       if (fd_ != -1)\n> +               close(fd_);\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> index f5bcf107d7aa..0a5bf7fdbeb7 100644\n> --- a/src/libcamera/framebuffer.cpp\n> +++ b/src/libcamera/framebuffer.cpp\n> @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()\n>   * offset and length.\n>   *\n>   * To support DMA access, planes are associated with dmabuf objects represented\n> - * by FileDescriptor handles. The Plane class doesn't handle mapping of the\n> - * memory to the CPU, but applications and IPAs may use the dmabuf file\n> - * descriptors to map the plane memory with mmap() and access its contents.\n> + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to\n> + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map\n> + * the plane memory with mmap() and access its contents.\n>   *\n>   * \\todo Specify how an application shall decide whether to use a single or\n>   * multiple dmabufs, based on the camera requirements.\n> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> index 82ec9b20a411..0a259305afa2 100644\n> --- a/src/libcamera/ipa_data_serializer.cpp\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)\n>   *\n>   * \\todo Harden the vector and map deserializer\n>   *\n> - * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> + * \\todo For SharedFDs, instead of storing a validity flag, store an\n>   * index into the fd array. This will allow us to use views instead of copying.\n>   */\n>\n> @@ -112,7 +112,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() can be used if the object type \\a T and its\n> - * members don't have any FileDescriptor.\n> + * members don't have any SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -132,7 +132,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() can be used if the object type \\a T and its\n> - * members don't have any FileDescriptor.\n> + * members don't have any SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -143,7 +143,7 @@ namespace {\n>  /**\n>   * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n>   *     const std::vector<uint8_t> &data,\n> - *     const std::vector<FileDescriptor> &fds,\n> + *     const std::vector<SharedFD> &fds,\n>   *     ControlSerializer *cs = nullptr)\n>   * \\brief Deserialize byte vector and fd vector into an object\n>   * \\tparam T Type of object to deserialize to\n> @@ -152,7 +152,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() (or the iterator version) must be used if\n> - * the object type \\a T or its members contain FileDescriptor.\n> + * the object type \\a T or its members contain SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -164,8 +164,8 @@ namespace {\n>   * \\fn template<typename T> IPADataSerializer::deserialize(\n>   *     std::vector<uint8_t>::const_iterator dataBegin,\n>   *     std::vector<uint8_t>::const_iterator dataEnd,\n> - *     std::vector<FileDescriptor>::const_iterator fdsBegin,\n> - *     std::vector<FileDescriptor>::const_iterator fdsEnd,\n> + *     std::vector<SharedFD>::const_iterator fdsBegin,\n> + *     std::vector<SharedFD>::const_iterator fdsEnd,\n>   *     ControlSerializer *cs = nullptr)\n>   * \\brief Deserialize byte vector and fd vector into an object\n>   * \\tparam T Type of object to deserialize to\n> @@ -176,7 +176,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() (or the vector version) must be used if\n> - * the object type \\a T or its members contain FileDescriptor.\n> + * the object type \\a T or its members contain SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -189,7 +189,7 @@ namespace {\n>  #define DEFINE_POD_SERIALIZER(type)                                    \\\n>                                                                         \\\n>  template<>                                                             \\\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>          \\\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>                \\\n>  IPADataSerializer<type>::serialize(const type &data,                   \\\n>                                   [[maybe_unused]] ControlSerializer *cs) \\\n>  {                                                                      \\\n> @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n>                                                                         \\\n>  template<>                                                             \\\n>  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> -                                         [[maybe_unused]] const std::vector<FileDescriptor> &fds, \\\n> +                                         [[maybe_unused]] const std::vector<SharedFD> &fds, \\\n>                                           ControlSerializer *cs)        \\\n>  {                                                                      \\\n>         return deserialize(data.cbegin(), data.end(), cs);              \\\n> @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n>  template<>                                                             \\\n>  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n>                                           std::vector<uint8_t>::const_iterator dataEnd, \\\n> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \\\n> -                                         [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \\\n> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \\\n> +                                         [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \\\n>                                           ControlSerializer *cs)        \\\n>  {                                                                      \\\n>         return deserialize(dataBegin, dataEnd, cs);                     \\\n> @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)\n>   * function parameter serdes).\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<std::string>::serialize(const std::string &data,\n>                                           [[maybe_unused]] ControlSerializer *cs)\n>  {\n> @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n>  template<>\n>  std::string\n>  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n> -                                           [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +                                           [[maybe_unused]] const std::vector<SharedFD> &fds,\n>                                             [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         return { data.cbegin(), data.cend() };\n> @@ -288,8 +288,8 @@ template<>\n>  std::string\n>  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                             std::vector<uint8_t>::const_iterator dataEnd,\n> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                             [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         return { dataBegin, dataEnd };\n> @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n>   * be used. The serialized ControlInfoMap will have zero length.\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)\n>  {\n>         if (!cs)\n> @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n>  template<>\n>  ControlList\n>  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> -                                           [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +                                           [[maybe_unused]] const std::vector<SharedFD> &fds,\n>                                             ControlSerializer *cs)\n>  {\n>         return deserialize(data.cbegin(), data.end(), cs);\n> @@ -417,8 +417,8 @@ template<>\n>  ControlList\n>  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                             std::vector<uint8_t>::const_iterator dataEnd,\n> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                           [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                           [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                             ControlSerializer *cs)\n>  {\n>         return deserialize(dataBegin, dataEnd, cs);\n> @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator\n>   * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,\n>                                              ControlSerializer *cs)\n>  {\n> @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n>  template<>\n>  ControlInfoMap\n>  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> -                                              [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +                                              [[maybe_unused]] const std::vector<SharedFD> &fds,\n>                                                ControlSerializer *cs)\n>  {\n>         return deserialize(data.cbegin(), data.end(), cs);\n> @@ -503,30 +503,30 @@ template<>\n>  ControlInfoMap\n>  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                                std::vector<uint8_t>::const_iterator dataEnd,\n> -                                              [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                              [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                              [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                              [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                                ControlSerializer *cs)\n>  {\n>         return deserialize(dataBegin, dataEnd, cs);\n>  }\n>\n>  /*\n> - * FileDescriptors are serialized into four bytes that tells if the\n> - * FileDescriptor is valid or not. If it is valid, then for serialization\n> - * the fd will be written to the fd vector, or for deserialization the\n> - * fd vector const_iterator will be valid.\n> + * SharedFD instances are serialized into four bytes that tells if the SharedFD\n> + * is valid or not. If it is valid, then for serialization the fd will be\n> + * written to the fd vector, or for deserialization the fd vector const_iterator\n> + * will be valid.\n>   *\n>   * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n>   * also allows us to simply send the entire fd vector into the deserializer\n>   * and it will be recursively consumed as necessary.\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> -                                            [[maybe_unused]] ControlSerializer *cs)\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,\n> +                                      [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         std::vector<uint8_t> dataVec;\n> -       std::vector<FileDescriptor> fdVec;\n> +       std::vector<SharedFD> fdVec;\n>\n>         /*\n>          * Store as uint32_t to prepare for conversion from validity flag\n> @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n>  }\n>\n>  template<>\n> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> -                                                             [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> -                                                             std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                                             std::vector<FileDescriptor>::const_iterator fdsEnd,\n> -                                                             [[maybe_unused]] ControlSerializer *cs)\n> +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> +                                                 [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> +                                                 std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                                 std::vector<SharedFD>::const_iterator fdsEnd,\n> +                                                 [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         ASSERT(std::distance(dataBegin, dataEnd) >= 4);\n>\n> @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s\n>\n>         ASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n>\n> -       return valid ? *fdsBegin : FileDescriptor();\n> +       return valid ? *fdsBegin : SharedFD();\n>  }\n>\n>  template<>\n> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,\n> -                                                             const std::vector<FileDescriptor> &fds,\n> -                                                             [[maybe_unused]] ControlSerializer *cs)\n> +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,\n> +                                                 const std::vector<SharedFD> &fds,\n> +                                                 [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n>  }\n> @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<\n>  /*\n>   * FrameBuffer::Plane is serialized as:\n>   *\n> - * 4 byte  - FileDescriptor\n> + * 4 byte  - SharedFD\n>   * 4 bytes - uint32_t Offset\n>   * 4 bytes - uint32_t Length\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,\n>                                                  [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         std::vector<uint8_t> dataVec;\n> -       std::vector<FileDescriptor> fdsVec;\n> +       std::vector<SharedFD> fdsVec;\n>\n>         std::vector<uint8_t> fdBuf;\n> -       std::vector<FileDescriptor> fdFds;\n> +       std::vector<SharedFD> fdFds;\n>         std::tie(fdBuf, fdFds) =\n> -               IPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +               IPADataSerializer<SharedFD>::serialize(data.fd);\n>         dataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n>         fdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n>\n> @@ -597,13 +597,13 @@ template<>\n>  FrameBuffer::Plane\n>  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                                                    std::vector<uint8_t>::const_iterator dataEnd,\n> -                                                  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                                                  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                                                  std::vector<SharedFD>::const_iterator fdsBegin,\n> +                                                  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                                                    [[maybe_unused]] ControlSerializer *cs)\n>  {\n>         FrameBuffer::Plane ret;\n>\n> -       ret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,\n> +       ret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,\n>                                                                 fdsBegin, fdsBegin + 1);\n>         ret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n>         ret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);\n> @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i\n>  template<>\n>  FrameBuffer::Plane\n>  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,\n> -                                                  const std::vector<FileDescriptor> &fds,\n> +                                                  const std::vector<SharedFD> &fds,\n>                                                    ControlSerializer *cs)\n>  {\n>         return deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp\n> index ad870fd4137f..3b47032de0a2 100644\n> --- a/src/libcamera/ipc_pipe.cpp\n> +++ b/src/libcamera/ipc_pipe.cpp\n> @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)\n>         data_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),\n>                                      payload.data.end());\n>         for (int32_t &fd : payload.fds)\n> -               fds_.push_back(FileDescriptor(std::move(fd)));\n> +               fds_.push_back(SharedFD(std::move(fd)));\n>  }\n>\n>  /**\n> @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const\n>                        data_.data(), data_.size());\n>         }\n>\n> -       for (const FileDescriptor &fd : fds_)\n> +       for (const SharedFD &fd : fds_)\n>                 payload.fds.push_back(fd.fd());\n>\n>         return payload;\n> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> index ffa51a0c65ca..ea8243912a29 100644\n> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> @@ -12,7 +12,7 @@\n>  #include <queue>\n>  #include <unordered_set>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/control_ids.h>\n> @@ -228,7 +228,7 @@ public:\n>\n>         /* DMAHEAP allocation helper. */\n>         RPi::DmaHeap dmaHeap_;\n> -       FileDescriptor lsTable_;\n> +       SharedFD lsTable_;\n>\n>         std::unique_ptr<DelayedControls> delayedCtrls_;\n>         bool sensorMetadata_;\n> @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>                 if (!fd.isValid())\n>                         return -ENOMEM;\n>\n> -               lsTable_ = FileDescriptor(std::move(fd));\n> +               lsTable_ = SharedFD(std::move(fd));\n>\n>                 /* Allow the IPA to mmap the LS table via the file descriptor. */\n>                 /*\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 3966483a365f..97d431071def 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -22,8 +22,8 @@\n>  #include <linux/version.h>\n>\n>  #include <libcamera/base/event_notifier.h>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/unique_fd.h>\n>  #include <libcamera/base/utils.h>\n>\n> @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>                         return nullptr;\n>\n>                 FrameBuffer::Plane plane;\n> -               plane.fd = FileDescriptor(std::move(fd));\n> +               plane.fd = SharedFD(std::move(fd));\n>                 /*\n>                  * V4L2 API doesn't provide dmabuf offset information of plane.\n>                  * Set 0 as a placeholder offset.\n> @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>                 ASSERT(numPlanes == 1u);\n>\n>                 planes.resize(formatInfo_->numPlanes());\n> -               const FileDescriptor &fd = planes[0].fd;\n> +               const SharedFD &fd = planes[0].fd;\n>                 size_t offset = 0;\n>\n>                 for (auto [i, plane] : utils::enumerate(planes)) {\n> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> index 9817fd393d59..586347829845 100644\n> --- a/src/v4l2/v4l2_camera.h\n> +++ b/src/v4l2/v4l2_camera.h\n> @@ -11,8 +11,8 @@\n>  #include <mutex>\n>  #include <utility>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/semaphore.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/framebuffer.h>\n> diff --git a/test/meson.build b/test/meson.build\n> index 42dfbc1f8ee9..daaa3862cdd6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -40,7 +40,6 @@ internal_tests = [\n>      ['event-dispatcher',                'event-dispatcher.cpp'],\n>      ['event-thread',                    'event-thread.cpp'],\n>      ['file',                            'file.cpp'],\n> -    ['file-descriptor',                 'file-descriptor.cpp'],\n>      ['flags',                           'flags.cpp'],\n>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],\n>      ['mapped-buffer',                   'mapped-buffer.cpp'],\n> @@ -49,6 +48,7 @@ internal_tests = [\n>      ['object-delete',                   'object-delete.cpp'],\n>      ['object-invoke',                   'object-invoke.cpp'],\n>      ['pixel-format',                    'pixel-format.cpp'],\n> +    ['shared-fd',                       'shared-fd.cpp'],\n>      ['signal-threads',                  'signal-threads.cpp'],\n>      ['threads',                         'threads.cpp'],\n>      ['timer',                           'timer.cpp'],\n> diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp\n> index 5fcdcb8eae92..d2050a868b38 100644\n> --- a/test/serialization/ipa_data_serializer_test.cpp\n> +++ b/test/serialization/ipa_data_serializer_test.cpp\n> @@ -53,7 +53,7 @@ template<typename T>\n>  int testPodSerdes(T in)\n>  {\n>         std::vector<uint8_t> buf;\n> -       std::vector<FileDescriptor> fds;\n> +       std::vector<SharedFD> fds;\n>\n>         std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);\n>         T out = IPADataSerializer<T>::deserialize(buf, fds);\n> @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,\n>                      ControlSerializer *cs = nullptr)\n>  {\n>         std::vector<uint8_t> buf;\n> -       std::vector<FileDescriptor> fds;\n> +       std::vector<SharedFD> fds;\n>\n>         std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);\n>         std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);\n> @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,\n>                   ControlSerializer *cs = nullptr)\n>  {\n>         std::vector<uint8_t> buf;\n> -       std::vector<FileDescriptor> fds;\n> +       std::vector<SharedFD> fds;\n>\n>         std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);\n>         std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);\n> @@ -198,7 +198,7 @@ private:\n>                 ControlSerializer cs(ControlSerializer::Role::Proxy);\n>\n>                 /*\n> -                * We don't test FileDescriptor serdes because it dup()s, so we\n> +                * We don't test SharedFD serdes because it dup()s, so we\n>                  * can't check for equality.\n>                  */\n>                 std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };\n> @@ -219,7 +219,7 @@ private:\n>                 };\n>\n>                 std::vector<uint8_t> buf;\n> -               std::vector<FileDescriptor> fds;\n> +               std::vector<SharedFD> fds;\n>\n>                 if (testVectorSerdes(vecUint8) != TestPass)\n>                         return TestFail;\n> @@ -291,7 +291,7 @@ private:\n>                         { { \"a\", { 1, 2, 3 } }, { \"b\", { 4, 5, 6 } }, { \"c\", { 7, 8, 9 } } };\n>\n>                 std::vector<uint8_t> buf;\n> -               std::vector<FileDescriptor> fds;\n> +               std::vector<SharedFD> fds;\n>\n>                 if (testMapSerdes(mapUintStr) != TestPass)\n>                         return TestFail;\n> @@ -359,7 +359,7 @@ private:\n>                 std::string strEmpty = \"\";\n>\n>                 std::vector<uint8_t> buf;\n> -               std::vector<FileDescriptor> fds;\n> +               std::vector<SharedFD> fds;\n>\n>                 if (testPodSerdes(u32min) != TestPass)\n>                         return TestFail;\n> diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp\n> similarity index 80%\n> rename from test/file-descriptor.cpp\n> rename to test/shared-fd.cpp\n> index 76badc4c5fad..60e5d0aaa395 100644\n> --- a/test/file-descriptor.cpp\n> +++ b/test/shared-fd.cpp\n> @@ -2,7 +2,7 @@\n>  /*\n>   * Copyright (C) 2019, Google Inc.\n>   *\n> - * file_descriptor.cpp - FileDescriptor test\n> + * shared_fd.cpp - SharedFD test\n>   */\n>\n>  #include <fcntl.h>\n> @@ -11,7 +11,7 @@\n>  #include <sys/types.h>\n>  #include <unistd.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/utils.h>\n>\n>  #include \"test.h\"\n> @@ -19,7 +19,7 @@\n>  using namespace libcamera;\n>  using namespace std;\n>\n> -class FileDescriptorTest : public Test\n> +class SharedFDTest : public Test\n>  {\n>  protected:\n>         int init()\n> @@ -43,8 +43,8 @@ protected:\n>\n>         int run()\n>         {\n> -               /* Test creating empty FileDescriptor. */\n> -               desc1_ = new FileDescriptor();\n> +               /* Test creating empty SharedFD. */\n> +               desc1_ = new SharedFD();\n>\n>                 if (desc1_->fd() != -1) {\n>                         std::cout << \"Failed fd numerical check (default constructor)\"\n> @@ -56,10 +56,10 @@ protected:\n>                 desc1_ = nullptr;\n>\n>                 /*\n> -                * Test creating FileDescriptor by copying numerical file\n> +                * Test creating SharedFD by copying numerical file\n>                  * descriptor.\n>                  */\n> -               desc1_ = new FileDescriptor(fd_);\n> +               desc1_ = new SharedFD(fd_);\n>                 if (desc1_->fd() == fd_) {\n>                         std::cout << \"Failed fd numerical check (lvalue ref constructor)\"\n>                                   << std::endl;\n> @@ -84,13 +84,13 @@ protected:\n>                 }\n>\n>                 /*\n> -                * Test creating FileDescriptor by taking ownership of\n> +                * Test creating SharedFD by taking ownership of\n>                  * numerical file descriptor.\n>                  */\n>                 int dupFd = dup(fd_);\n>                 int dupFdCopy = dupFd;\n>\n> -               desc1_ = new FileDescriptor(std::move(dupFd));\n> +               desc1_ = new SharedFD(std::move(dupFd));\n>                 if (desc1_->fd() != dupFdCopy) {\n>                         std::cout << \"Failed fd numerical check (rvalue ref constructor)\"\n>                                   << std::endl;\n> @@ -114,9 +114,9 @@ protected:\n>                         return TestFail;\n>                 }\n>\n> -               /* Test creating FileDescriptor from other FileDescriptor. */\n> -               desc1_ = new FileDescriptor(fd_);\n> -               desc2_ = new FileDescriptor(*desc1_);\n> +               /* Test creating SharedFD from other SharedFD. */\n> +               desc1_ = new SharedFD(fd_);\n> +               desc2_ = new SharedFD(*desc1_);\n>\n>                 if (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {\n>                         std::cout << \"Failed fd numerical check (copy constructor)\"\n> @@ -142,10 +142,10 @@ protected:\n>                 delete desc2_;\n>                 desc2_ = nullptr;\n>\n> -               /* Test creating FileDescriptor by taking over other FileDescriptor. */\n> -               desc1_ = new FileDescriptor(fd_);\n> +               /* Test creating SharedFD by taking over other SharedFD. */\n> +               desc1_ = new SharedFD(fd_);\n>                 fd = desc1_->fd();\n> -               desc2_ = new FileDescriptor(std::move(*desc1_));\n> +               desc2_ = new SharedFD(std::move(*desc1_));\n>\n>                 if (desc1_->fd() != -1 || desc2_->fd() != fd) {\n>                         std::cout << \"Failed fd numerical check (move constructor)\"\n> @@ -164,9 +164,9 @@ protected:\n>                 delete desc2_;\n>                 desc2_ = nullptr;\n>\n> -               /* Test creating FileDescriptor by copy assignment. */\n> -               desc1_ = new FileDescriptor();\n> -               desc2_ = new FileDescriptor(fd_);\n> +               /* Test creating SharedFD by copy assignment. */\n> +               desc1_ = new SharedFD();\n> +               desc2_ = new SharedFD(fd_);\n>\n>                 fd = desc2_->fd();\n>                 *desc1_ = *desc2_;\n> @@ -188,9 +188,9 @@ protected:\n>                 delete desc2_;\n>                 desc2_ = nullptr;\n>\n> -               /* Test creating FileDescriptor by move assignment. */\n> -               desc1_ = new FileDescriptor();\n> -               desc2_ = new FileDescriptor(fd_);\n> +               /* Test creating SharedFD by move assignment. */\n> +               desc1_ = new SharedFD();\n> +               desc2_ = new SharedFD(fd_);\n>\n>                 fd = desc2_->fd();\n>                 *desc1_ = std::move(*desc2_);\n> @@ -237,7 +237,7 @@ private:\n>\n>         int fd_;\n>         ino_t inodeNr_;\n> -       FileDescriptor *desc1_, *desc2_;\n> +       SharedFD *desc1_, *desc2_;\n>  };\n>\n> -TEST_REGISTER(FileDescriptorTest)\n> +TEST_REGISTER(SharedFDTest)\n> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> index d856339aa9ee..c37c4941b528 100644\n> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>  void {{proxy_name}}::{{method.mojom_name}}IPC(\n>         std::vector<uint8_t>::const_iterator data,\n>         size_t dataSize,\n> -       [[maybe_unused]] const std::vector<FileDescriptor> &fds)\n> +       [[maybe_unused]] const std::vector<SharedFD> &fds)\n>  {\n>  {%- for param in method.parameters %}\n>         {{param|name}} {{param.mojom_name}};\n> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> index ce396c183d0c..c308dd10c7e5 100644\n> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> @@ -64,7 +64,7 @@ private:\n>         void {{method.mojom_name}}IPC(\n>                 std::vector<uint8_t>::const_iterator data,\n>                 size_t dataSize,\n> -               const std::vector<FileDescriptor> &fds);\n> +               const std::vector<SharedFD> &fds);\n>  {% endfor %}\n>\n>         /* Helper class to invoke async functions in another thread. */\n> diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> index ebcd2aaaafae..bac826a74c2d 100644\n> --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> @@ -54,7 +54,7 @@\n>  {%- for param in params %}\n>         std::vector<uint8_t> {{param.mojom_name}}Buf;\n>  {%- if param|has_fd %}\n> -       std::vector<FileDescriptor> {{param.mojom_name}}Fds;\n> +       std::vector<SharedFD> {{param.mojom_name}}Fds;\n>         std::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =\n>  {%- else %}\n>         std::tie({{param.mojom_name}}Buf, std::ignore) =\n> diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> index b8ef8e7b974e..77bae36fe6b7 100644\n> --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> @@ -40,7 +40,7 @@\n>                 retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n>  {%- elif field|is_fd %}\n>                 std::vector<uint8_t> {{field.mojom_name}};\n> -               std::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> +               std::vector<SharedFD> {{field.mojom_name}}Fds;\n>                 std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n>                         IPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});\n>                 retData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> @@ -58,7 +58,7 @@\n>  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}\n>                 std::vector<uint8_t> {{field.mojom_name}};\n>         {%- if field|has_fd %}\n> -               std::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> +               std::vector<SharedFD> {{field.mojom_name}}Fds;\n>                 std::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n>         {%- else %}\n>                 std::tie({{field.mojom_name}}, std::ignore) =\n> @@ -177,7 +177,7 @@\n>   # \\a struct.\n>   #}\n>  {%- macro serializer(struct, namespace) %}\n> -       static std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +       static std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>         serialize(const {{struct|name_full}} &data,\n>  {%- if struct|needs_control_serializer %}\n>                   ControlSerializer *cs)\n> @@ -187,7 +187,7 @@\n>         {\n>                 std::vector<uint8_t> retData;\n>  {%- if struct|has_fd %}\n> -               std::vector<FileDescriptor> retFds;\n> +               std::vector<SharedFD> retFds;\n>  {%- endif %}\n>  {%- for field in struct.fields %}\n>  {{serializer_field(field, namespace, loop)}}\n> @@ -210,7 +210,7 @@\n>  {%- macro deserializer_fd(struct, namespace) %}\n>         static {{struct|name_full}}\n>         deserialize(std::vector<uint8_t> &data,\n> -                   std::vector<FileDescriptor> &fds,\n> +                   std::vector<SharedFD> &fds,\n>  {%- if struct|needs_control_serializer %}\n>                     ControlSerializer *cs)\n>  {%- else %}\n> @@ -224,8 +224,8 @@\n>         static {{struct|name_full}}\n>         deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                     std::vector<uint8_t>::const_iterator dataEnd,\n> -                   std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                   std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                   std::vector<SharedFD>::const_iterator fdsBegin,\n> +                   std::vector<SharedFD>::const_iterator fdsEnd,\n>  {%- if struct|needs_control_serializer %}\n>                     ControlSerializer *cs)\n>  {%- else %}\n> @@ -234,7 +234,7 @@\n>         {\n>                 {{struct|name_full}} ret;\n>                 std::vector<uint8_t>::const_iterator m = dataBegin;\n> -               std::vector<FileDescriptor>::const_iterator n = fdsBegin;\n> +               std::vector<SharedFD>::const_iterator n = fdsBegin;\n>\n>                 size_t dataSize = std::distance(dataBegin, dataEnd);\n>                 [[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);\n> @@ -255,7 +255,7 @@\n>  {%- macro deserializer_fd_simple(struct, namespace) %}\n>         static {{struct|name_full}}\n>         deserialize(std::vector<uint8_t> &data,\n> -                   [[maybe_unused]] std::vector<FileDescriptor> &fds,\n> +                   [[maybe_unused]] std::vector<SharedFD> &fds,\n>                     ControlSerializer *cs = nullptr)\n>         {\n>                 return IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);\n> @@ -264,8 +264,8 @@\n>         static {{struct|name_full}}\n>         deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>                     std::vector<uint8_t>::const_iterator dataEnd,\n> -                   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -                   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +                   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +                   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>                     ControlSerializer *cs = nullptr)\n>         {\n>                 return IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);\n> diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py\n> index c609f4e5c062..753bfc734e56 100644\n> --- a/utils/ipc/generators/mojom_libcamera_generator.py\n> +++ b/utils/ipc/generators/mojom_libcamera_generator.py\n> @@ -77,7 +77,7 @@ def GetDefaultValue(element):\n>      if mojom.IsEnumKind(element.kind):\n>          return f'static_cast<{element.kind.mojom_name}>(0)'\n>      if isinstance(element.kind, mojom.Struct) and \\\n> -       element.kind.mojom_name == 'FileDescriptor':\n> +       element.kind.mojom_name == 'SharedFD':\n>          return '-1'\n>      return ''\n>\n> @@ -140,7 +140,7 @@ def HasFd(element):\n>          types = GetAllTypes(element)\n>      else:\n>          types = GetAllTypes(element.kind)\n> -    return \"FileDescriptor\" in types or (attrs is not None and \"hasFd\" in attrs)\n> +    return \"SharedFD\" in types or (attrs is not None and \"hasFd\" in attrs)\n>\n>  def WithDefaultValues(element):\n>      return [x for x in element if HasDefaultValue(x)]\n> @@ -221,7 +221,7 @@ def IsEnum(element):\n>      return mojom.IsEnumKind(element.kind)\n>\n>  def IsFd(element):\n> -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"FileDescriptor\"\n> +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"SharedFD\"\n>\n>  def IsMap(element):\n>      return mojom.IsMapKind(element.kind)\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","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 07C0BBF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Nov 2021 14:04:42 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3C0B7605A6;\n\tMon, 29 Nov 2021 15:04:41 +0100 (CET)","from mail-ed1-x529.google.com (mail-ed1-x529.google.com\n\t[IPv6:2a00:1450:4864:20::529])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F29E560592\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 15:04:39 +0100 (CET)","by mail-ed1-x529.google.com with SMTP id g14so72324170edb.8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 06:04:39 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=chromium.org header.i=@chromium.org\n\theader.b=\"eTXWUlS+\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org;\n\ts=google; \n\th=mime-version:references:in-reply-to:from:date:message-id:subject:to\n\t:cc; bh=ILuBhbo+Fjqg4kY8kNnqLLrAJJP2Eau+jU6J40YOGRs=;\n\tb=eTXWUlS+K9kKZULXFYw5HTyGbNSBtq8P+ObYg9q+MB+MX5maFN+ME3S7lJHlSSzXjC\n\t4aj3Ak0Lk1BhLq6Gta7Ay+iQM0fzXh23nziNsTY5HZLJHuey58zfol0HSApCjX9IkqLw\n\texS/bMnlfcOX2YtNS5UQhn8DUVCwG9U5j18cg=","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=x-gm-message-state:mime-version:references:in-reply-to:from:date\n\t:message-id:subject:to:cc;\n\tbh=ILuBhbo+Fjqg4kY8kNnqLLrAJJP2Eau+jU6J40YOGRs=;\n\tb=WFx7LoBjPFd30/SHnALIrDIqeTwQifpaE15FTFM/cCio+gs+S4DZHQ8fwaConjyB+V\n\tVxgVh1y3PLG4Q1MphnJk7TAAbyAecdsexAXiEiaQI5s/dUNSoZsrw8N2iL/PzV3Jf7z/\n\tsn3dNpW2cRM0BvoxNPK62nFZxE/KkywvUqqaucqiGizVtk+v3Ryf/W1jnDBaGsmfKNnF\n\tUE4AYb0gjFkv0zZXkrqSphW4/1dOImxX6Q4mYMyxQVw3SGsz6XwjdwxcmfIGzy1yaiCN\n\t9G5nJCkkSWRftjD8oWCMgukZ8kLRODfG9ET8EYWrHzVjKCbMofLmI4VJiiOsHZcm97CP\n\tggkg==","X-Gm-Message-State":"AOAM532s7xy9nG701fP2KyvPJxT0JTv59DS+86AUa/aUtZiXa2LeY+MU\n\tuNHFFA8phX7h7VTr7Qvwv3pJfGTaoWCMqBLYZHReq1mrdFA=","X-Google-Smtp-Source":"ABdhPJxH6UCMwusQ5ehRX/njnQzsfYTVxMdeK1l7wMFCugaTfynxVq9jC7IMa/XRNLGdrAn7vQk+lVJNuxMc2u6VeT8=","X-Received":"by 2002:a05:6402:2552:: with SMTP id\n\tl18mr74998032edb.368.1638194678296; \n\tMon, 29 Nov 2021 06:04:38 -0800 (PST)","MIME-Version":"1.0","References":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>\n\t<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","In-Reply-To":"<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","From":"Hirokazu Honda <hiroh@chromium.org>","Date":"Mon, 29 Nov 2021 23:04:26 +0900","Message-ID":"<CAO5uPHNgvKxamPx32V-NCFhPBS_0qpBes=4WJovqPA93grExcA@mail.gmail.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":21349,"web_url":"https://patchwork.libcamera.org/comment/21349/","msgid":"<20211129160055.p74ha6iidz7gxxqm@uno.localdomain>","date":"2021-11-29T16:00:55","subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent\n\nOn Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:\n> Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.\n> Rename it to SharedFD.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nmakes sense!\n\nThanks\n  j\n\n> ---\n>  include/libcamera/base/file.h                 |   4 +-\n>  include/libcamera/base/meson.build            |   2 +-\n>  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-\n>  include/libcamera/framebuffer.h               |   4 +-\n>  .../libcamera/internal/ipa_data_serializer.h  |  40 +--\n>  include/libcamera/internal/ipc_pipe.h         |   8 +-\n>  include/libcamera/ipa/core.mojom              |   6 +-\n>  include/libcamera/ipa/raspberrypi.mojom       |   2 +-\n>  src/android/camera_device.cpp                 |   2 +-\n>  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-\n>  src/libcamera/base/file.cpp                   |   6 +-\n>  src/libcamera/base/file_descriptor.cpp        | 266 ------------------\n>  src/libcamera/base/meson.build                |   2 +-\n>  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++\n>  src/libcamera/framebuffer.cpp                 |   6 +-\n>  src/libcamera/ipa_data_serializer.cpp         | 100 +++----\n>  src/libcamera/ipc_pipe.cpp                    |   4 +-\n>  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-\n>  src/libcamera/v4l2_videodevice.cpp            |   6 +-\n>  src/v4l2/v4l2_camera.h                        |   2 +-\n>  test/meson.build                              |   2 +-\n>  .../ipa_data_serializer_test.cpp              |  14 +-\n>  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--\n>  .../module_ipa_proxy.cpp.tmpl                 |   2 +-\n>  .../module_ipa_proxy.h.tmpl                   |   2 +-\n>  .../libcamera_templates/proxy_functions.tmpl  |   2 +-\n>  .../libcamera_templates/serializer.tmpl       |  22 +-\n>  .../generators/mojom_libcamera_generator.py   |   6 +-\n>  28 files changed, 422 insertions(+), 426 deletions(-)\n>  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)\n>  delete mode 100644 src/libcamera/base/file_descriptor.cpp\n>  create mode 100644 src/libcamera/base/shared_fd.cpp\n>  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)\n>\n> diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h\n> index 47769da7abc2..691b52d6ab2d 100644\n> --- a/include/libcamera/base/file.h\n> +++ b/include/libcamera/base/file.h\n> @@ -21,7 +21,7 @@\n>\n>  namespace libcamera {\n>\n> -class FileDescriptor;\n> +class SharedFD;\n>\n>  class File\n>  {\n> @@ -69,7 +69,7 @@ public:\n>  \tbool unmap(uint8_t *addr);\n>\n>  \tstatic bool exists(const std::string &name);\n> -\tstatic ino_t inode(const FileDescriptor &fd);\n> +\tstatic ino_t inode(const SharedFD &fd);\n>\n>  private:\n>  \tLIBCAMERA_DISABLE_COPY(File)\n> diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\n> index cca374a769cc..112420dab225 100644\n> --- a/include/libcamera/base/meson.build\n> +++ b/include/libcamera/base/meson.build\n> @@ -11,13 +11,13 @@ libcamera_base_headers = files([\n>      'event_dispatcher_poll.h',\n>      'event_notifier.h',\n>      'file.h',\n> -    'file_descriptor.h',\n>      'flags.h',\n>      'log.h',\n>      'message.h',\n>      'object.h',\n>      'private.h',\n>      'semaphore.h',\n> +    'shared_fd.h',\n>      'signal.h',\n>      'span.h',\n>      'thread.h',\n> diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h\n> similarity index 55%\n> rename from include/libcamera/base/file_descriptor.h\n> rename to include/libcamera/base/shared_fd.h\n> index 12a43f95d414..a786885ceb32 100644\n> --- a/include/libcamera/base/file_descriptor.h\n> +++ b/include/libcamera/base/shared_fd.h\n> @@ -2,7 +2,7 @@\n>  /*\n>   * Copyright (C) 2019, Google Inc.\n>   *\n> - * file_descriptor.h - File descriptor wrapper\n> + * shared_fd.h - File descriptor wrapper with shared ownership\n>   */\n>\n>  #pragma once\n> @@ -13,18 +13,18 @@ namespace libcamera {\n>\n>  class UniqueFD;\n>\n> -class FileDescriptor final\n> +class SharedFD final\n>  {\n>  public:\n> -\texplicit FileDescriptor(const int &fd = -1);\n> -\texplicit FileDescriptor(int &&fd);\n> -\texplicit FileDescriptor(UniqueFD fd);\n> -\tFileDescriptor(const FileDescriptor &other);\n> -\tFileDescriptor(FileDescriptor &&other);\n> -\t~FileDescriptor();\n> +\texplicit SharedFD(const int &fd = -1);\n> +\texplicit SharedFD(int &&fd);\n> +\texplicit SharedFD(UniqueFD fd);\n> +\tSharedFD(const SharedFD &other);\n> +\tSharedFD(SharedFD &&other);\n> +\t~SharedFD();\n>\n> -\tFileDescriptor &operator=(const FileDescriptor &other);\n> -\tFileDescriptor &operator=(FileDescriptor &&other);\n> +\tSharedFD &operator=(const SharedFD &other);\n> +\tSharedFD &operator=(SharedFD &&other);\n>\n>  \tbool isValid() const { return fd_ != nullptr; }\n>  \tint fd() const { return fd_ ? fd_->fd() : -1; }\n> diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> index 2fbea9c5be16..357bbe189551 100644\n> --- a/include/libcamera/framebuffer.h\n> +++ b/include/libcamera/framebuffer.h\n> @@ -13,7 +13,7 @@\n>  #include <vector>\n>\n>  #include <libcamera/base/class.h>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/span.h>\n>\n>  namespace libcamera {\n> @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible\n>  public:\n>  \tstruct Plane {\n>  \t\tstatic constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();\n> -\t\tFileDescriptor fd;\n> +\t\tSharedFD fd;\n>  \t\tunsigned int offset = kInvalidOffset;\n>  \t\tunsigned int length;\n>  \t};\n> diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\n> index c2f602d5b7de..a87449c9be48 100644\n> --- a/include/libcamera/internal/ipa_data_serializer.h\n> +++ b/include/libcamera/internal/ipa_data_serializer.h\n> @@ -66,7 +66,7 @@ template<typename T>\n>  class IPADataSerializer\n>  {\n>  public:\n> -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  \tserialize(const T &data, ControlSerializer *cs = nullptr);\n>\n>  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> @@ -76,12 +76,12 @@ public:\n>  \t\t\t     ControlSerializer *cs = nullptr);\n>\n>  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t     const std::vector<FileDescriptor> &fds,\n> +\t\t\t     const std::vector<SharedFD> &fds,\n>  \t\t\t     ControlSerializer *cs = nullptr);\n>  \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t     std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t     std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t     ControlSerializer *cs = nullptr);\n>  };\n>\n> @@ -104,11 +104,11 @@ template<typename V>\n>  class IPADataSerializer<std::vector<V>>\n>  {\n>  public:\n> -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  \tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\tstd::vector<uint8_t> dataVec;\n> -\t\tstd::vector<FileDescriptor> fdsVec;\n> +\t\tstd::vector<SharedFD> fdsVec;\n>\n>  \t\t/* Serialize the length. */\n>  \t\tuint32_t vecLen = data.size();\n> @@ -117,7 +117,7 @@ public:\n>  \t\t/* Serialize the members. */\n>  \t\tfor (auto const &it : data) {\n>  \t\t\tstd::vector<uint8_t> dvec;\n> -\t\t\tstd::vector<FileDescriptor> fvec;\n> +\t\t\tstd::vector<SharedFD> fvec;\n>\n>  \t\t\tstd::tie(dvec, fvec) =\n>  \t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> @@ -141,11 +141,11 @@ public:\n>  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n> -\t\tstd::vector<FileDescriptor> fds;\n> +\t\tstd::vector<SharedFD> fds;\n>  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n>  \t}\n>\n> -\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> @@ -153,15 +153,15 @@ public:\n>\n>  \tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n>  \t\tstd::vector<V> ret(vecLen);\n>\n>  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n>  \t\tfor (uint32_t i = 0; i < vecLen; i++) {\n>  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n>  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> @@ -201,11 +201,11 @@ template<typename K, typename V>\n>  class IPADataSerializer<std::map<K, V>>\n>  {\n>  public:\n> -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  \tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\tstd::vector<uint8_t> dataVec;\n> -\t\tstd::vector<FileDescriptor> fdsVec;\n> +\t\tstd::vector<SharedFD> fdsVec;\n>\n>  \t\t/* Serialize the length. */\n>  \t\tuint32_t mapLen = data.size();\n> @@ -214,7 +214,7 @@ public:\n>  \t\t/* Serialize the members. */\n>  \t\tfor (auto const &it : data) {\n>  \t\t\tstd::vector<uint8_t> dvec;\n> -\t\t\tstd::vector<FileDescriptor> fvec;\n> +\t\t\tstd::vector<SharedFD> fvec;\n>\n>  \t\t\tstd::tie(dvec, fvec) =\n>  \t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> @@ -247,11 +247,11 @@ public:\n>  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n> -\t\tstd::vector<FileDescriptor> fds;\n> +\t\tstd::vector<SharedFD> fds;\n>  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n>  \t}\n>\n> -\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> @@ -259,8 +259,8 @@ public:\n>\n>  \tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\tstd::map<K, V> ret;\n> @@ -268,7 +268,7 @@ public:\n>  \t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n>\n>  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n>  \t\tfor (uint32_t i = 0; i < mapLen; i++) {\n>  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n>  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h\n> index 986f8d886fa6..ab5dd67c3813 100644\n> --- a/include/libcamera/internal/ipc_pipe.h\n> +++ b/include/libcamera/internal/ipc_pipe.h\n> @@ -9,7 +9,7 @@\n>\n>  #include <vector>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/signal.h>\n>\n>  #include \"libcamera/internal/ipc_unixsocket.h\"\n> @@ -33,17 +33,17 @@ public:\n>\n>  \tHeader &header() { return header_; }\n>  \tstd::vector<uint8_t> &data() { return data_; }\n> -\tstd::vector<FileDescriptor> &fds() { return fds_; }\n> +\tstd::vector<SharedFD> &fds() { return fds_; }\n>\n>  \tconst Header &header() const { return header_; }\n>  \tconst std::vector<uint8_t> &data() const { return data_; }\n> -\tconst std::vector<FileDescriptor> &fds() const { return fds_; }\n> +\tconst std::vector<SharedFD> &fds() const { return fds_; }\n>\n>  private:\n>  \tHeader header_;\n>\n>  \tstd::vector<uint8_t> data_;\n> -\tstd::vector<FileDescriptor> fds_;\n> +\tstd::vector<SharedFD> fds_;\n>  };\n>\n>  class IPCPipe\n> diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom\n> index f7eff0c7ab8c..74f3339e56f2 100644\n> --- a/include/libcamera/ipa/core.mojom\n> +++ b/include/libcamera/ipa/core.mojom\n> @@ -32,7 +32,7 @@ module libcamera;\n>   *   - This attribute instructs the build system that a (de)serializer is\n>   *     available for the type and there's no need to generate one\n>   * - hasFd - struct fields or empty structs only\n> - *   - Designate that this field or empty struct contains a FileDescriptor\n> + *   - Designate that this field or empty struct contains a SharedFD\n>   *\n>   * Rules:\n>   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is\n> @@ -60,7 +60,7 @@ module libcamera;\n>   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array\n>   *       member\n>   * - [skipHeader] and [skipSerdes] only work here in core.mojom\n> - * - If a field in a struct has a FileDescriptor, but is not explicitly\n> + * - If a field in a struct has a SharedFD, but is not explicitly\n>   *   defined so in mojom, then the field must be marked with the [hasFd]\n>   *   attribute\n>   *\n> @@ -71,7 +71,7 @@ module libcamera;\n>   */\n>  [skipSerdes, skipHeader] struct ControlInfoMap {};\n>  [skipSerdes, skipHeader] struct ControlList {};\n> -[skipSerdes, skipHeader] struct FileDescriptor {};\n> +[skipSerdes, skipHeader] struct SharedFD {};\n>\n>  [skipHeader] struct Point {\n>  \tint32 x;\n> diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> index e453d46cb14f..acd3cafe6c91 100644\n> --- a/include/libcamera/ipa/raspberrypi.mojom\n> +++ b/include/libcamera/ipa/raspberrypi.mojom\n> @@ -35,7 +35,7 @@ struct ISPConfig {\n>\n>  struct IPAConfig {\n>  \tuint32 transform;\n> -\tlibcamera.FileDescriptor lsTableHandle;\n> +\tlibcamera.SharedFD lsTableHandle;\n>  };\n>\n>  struct StartConfig {\n> diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\n> index f2e0bdbdbbf6..1938b10509fa 100644\n> --- a/src/android/camera_device.cpp\n> +++ b/src/android/camera_device.cpp\n> @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n>\n>  \tstd::vector<FrameBuffer::Plane> planes(buf.numPlanes());\n>  \tfor (size_t i = 0; i < buf.numPlanes(); ++i) {\n> -\t\tFileDescriptor fd{ camera3buffer->data[i] };\n> +\t\tSharedFD fd{ camera3buffer->data[i] };\n>  \t\tif (!fd.isValid()) {\n>  \t\t\tLOG(HAL, Fatal) << \"No valid fd\";\n>  \t\t\treturn nullptr;\n> diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> index c6aec09046f7..aaf629eeb3fc 100644\n> --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> @@ -15,8 +15,8 @@\n>\n>  #include <linux/bcm2835-isp.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/span.h>\n>\n>  #include <libcamera/control_ids.h>\n> @@ -164,7 +164,7 @@ private:\n>  \tbool processPending_;\n>\n>  \t/* LS table allocation passed in from the pipeline handler. */\n> -\tFileDescriptor lsTableHandle_;\n> +\tSharedFD lsTableHandle_;\n>  \tvoid *lsTable_;\n>\n>  \t/* Distinguish the first camera start from others. */\n> diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp\n> index 66c73c406198..3ca9839bb989 100644\n> --- a/src/libcamera/base/file.cpp\n> +++ b/src/libcamera/base/file.cpp\n> @@ -14,8 +14,8 @@\n>  #include <sys/types.h>\n>  #include <unistd.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  /**\n>   * \\file base/file.h\n> @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)\n>  }\n>\n>  /**\n> - * \\brief Retrieve the inode of a FileDescriptor\n> + * \\brief Retrieve the inode of a SharedFD\n>   *\n>   * \\return The file descriptor inode on success, or 0 on error\n>   */\n> -ino_t File::inode(const FileDescriptor &fd)\n> +ino_t File::inode(const SharedFD &fd)\n>  {\n>  \tif (!fd.isValid())\n>  \t\treturn 0;\n> diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp\n> deleted file mode 100644\n> index a83bf52c31e6..000000000000\n> --- a/src/libcamera/base/file_descriptor.cpp\n> +++ /dev/null\n> @@ -1,266 +0,0 @@\n> -/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> -/*\n> - * Copyright (C) 2019, Google Inc.\n> - *\n> - * file_descriptor.cpp - File descriptor wrapper\n> - */\n> -\n> -#include <libcamera/base/file_descriptor.h>\n> -\n> -#include <string.h>\n> -#include <sys/types.h>\n> -#include <unistd.h>\n> -#include <utility>\n> -\n> -#include <libcamera/base/log.h>\n> -#include <libcamera/base/unique_fd.h>\n> -\n> -/**\n> - * \\file base/file_descriptor.h\n> - * \\brief File descriptor wrapper\n> - */\n> -\n> -namespace libcamera {\n> -\n> -LOG_DEFINE_CATEGORY(FileDescriptor)\n> -\n> -/**\n> - * \\class FileDescriptor\n> - * \\brief RAII-style wrapper for file descriptors\n> - *\n> - * The FileDescriptor class provides RAII-style lifetime management of file\n> - * descriptors with an efficient mechanism for ownership sharing. At its core,\n> - * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> - * integer) with an RAII-style interface. The Descriptor is then implicitly\n> - * shared with all FileDescriptor instances constructed as copies.\n> - *\n> - * When constructed from a numerical file descriptor, the FileDescriptor\n> - * instance either duplicates or takes over the file descriptor:\n> - *\n> - * - The FileDescriptor(const int &) constructor duplicates the numerical file\n> - *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> - *   responsible for closing the original file descriptor, and the value\n> - *   returned by fd() will be different from the value passed to the\n> - *   constructor.\n> - *\n> - * - The FileDescriptor(int &&) constructor takes over the numerical file\n> - *   descriptor and wraps it in a Descriptor. The caller shall not touch the\n> - *   original file descriptor once the function returns, and the value returned\n> - *   by fd() will be identical to the value passed to the constructor.\n> - *\n> - * The copy constructor and assignment operator create copies that share the\n> - * Descriptor, while the move versions of those functions additionally make the\n> - * other FileDescriptor invalid. When the last FileDescriptor that references a\n> - * Descriptor is destroyed, the file descriptor is closed.\n> - *\n> - * The numerical file descriptor is available through the fd() function. All\n> - * FileDescriptor instances created as copies of a FileDescriptor will report\n> - * the same fd() value. Callers can perform operations on the fd(), but shall\n> - * never close it manually.\n> - */\n> -\n> -/**\n> - * \\brief Create a FileDescriptor copying a given \\a fd\n> - * \\param[in] fd File descriptor\n> - *\n> - * Construct a FileDescriptor from a numerical file descriptor by duplicating\n> - * the \\a fd, and take ownership of the copy. The original \\a fd is left\n> - * untouched, and the caller is responsible for closing it when appropriate.\n> - * The duplicated file descriptor will be closed automatically when all\n> - * FileDescriptor instances that reference it are destroyed.\n> - *\n> - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> - * the fd() function will return -1.\n> - */\n> -FileDescriptor::FileDescriptor(const int &fd)\n> -{\n> -\tif (fd < 0)\n> -\t\treturn;\n> -\n> -\tfd_ = std::make_shared<Descriptor>(fd, true);\n> -\tif (fd_->fd() < 0)\n> -\t\tfd_.reset();\n> -}\n> -\n> -/**\n> - * \\brief Create a FileDescriptor taking ownership of a given \\a fd\n> - * \\param[in] fd File descriptor\n> - *\n> - * Construct a FileDescriptor from a numerical file descriptor by taking\n> - * ownership of the \\a fd. The original \\a fd is set to -1 and shall not be\n> - * touched by the caller anymore. In particular, the caller shall not close the\n> - * original \\a fd manually. The duplicated file descriptor will be closed\n> - * automatically when all FileDescriptor instances that reference it are\n> - * destroyed.\n> - *\n> - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> - * the fd() function will return -1.\n> - */\n> -FileDescriptor::FileDescriptor(int &&fd)\n> -{\n> -\tif (fd < 0)\n> -\t\treturn;\n> -\n> -\tfd_ = std::make_shared<Descriptor>(fd, false);\n> -\t/*\n> -\t * The Descriptor constructor can't have failed here, as it took over\n> -\t * the fd without duplicating it. Just set the original fd to -1 to\n> -\t * implement move semantics.\n> -\t */\n> -\tfd = -1;\n> -}\n> -\n> -/**\n> - * \\brief Create a FileDescriptor taking ownership of a given UniqueFD \\a fd\n> - * \\param[in] fd UniqueFD\n> - *\n> - * Construct a FileDescriptor from UniqueFD by taking ownership of the \\a fd.\n> - * The original \\a fd becomes invalid.\n> - */\n> -FileDescriptor::FileDescriptor(UniqueFD fd)\n> -\t: FileDescriptor(fd.release())\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Copy constructor, create a FileDescriptor from a copy of \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Copying a FileDescriptor implicitly shares ownership of the wrapped file\n> - * descriptor. The original FileDescriptor is left untouched, and the caller is\n> - * responsible for destroying it when appropriate. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - */\n> -FileDescriptor::FileDescriptor(const FileDescriptor &other)\n> -\t: fd_(other.fd_)\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Move constructor, create a FileDescriptor by taking over \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> - * by \\a other to the new FileDescriptor. The \\a other FileDescriptor is\n> - * invalidated and its fd() function will return -1. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - */\n> -FileDescriptor::FileDescriptor(FileDescriptor &&other)\n> -\t: fd_(std::move(other.fd_))\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Destroy the FileDescriptor instance\n> - *\n> - * Destroying a FileDescriptor instance releases its reference to the wrapped\n> - * descriptor, if any. When the last instance that references a wrapped\n> - * descriptor is destroyed, the file descriptor is automatically closed.\n> - */\n> -FileDescriptor::~FileDescriptor()\n> -{\n> -}\n> -\n> -/**\n> - * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> - * copy of \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Copying a FileDescriptor creates a new reference to the wrapped file\n> - * descriptor owner by \\a other. If \\a other is invalid, *this will also be\n> - * invalid. The original FileDescriptor is left untouched, and the caller is\n> - * responsible for destroying it when appropriate. The wrapped file descriptor\n> - * will be closed automatically when all FileDescriptor instances that\n> - * reference it are destroyed.\n> - *\n> - * \\return A reference to this FileDescriptor\n> - */\n> -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)\n> -{\n> -\tfd_ = other.fd_;\n> -\n> -\treturn *this;\n> -}\n> -\n> -/**\n> - * \\brief Move assignment operator, replace the wrapped file descriptor by\n> - * taking over \\a other\n> - * \\param[in] other The other FileDescriptor\n> - *\n> - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> - * by \\a other to the new FileDescriptor. If \\a other is invalid, *this will\n> - * also be invalid. The \\a other FileDescriptor is invalidated and its fd()\n> - * function will return -1. The wrapped file descriptor will be closed\n> - * automatically when all FileDescriptor instances that reference it are\n> - * destroyed.\n> - *\n> - * \\return A reference to this FileDescriptor\n> - */\n> -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)\n> -{\n> -\tfd_ = std::move(other.fd_);\n> -\n> -\treturn *this;\n> -}\n> -\n> -/**\n> - * \\fn FileDescriptor::isValid()\n> - * \\brief Check if the FileDescriptor instance is valid\n> - * \\return True if the FileDescriptor is valid, false otherwise\n> - */\n> -\n> -/**\n> - * \\fn FileDescriptor::fd()\n> - * \\brief Retrieve the numerical file descriptor\n> - * \\return The numerical file descriptor, which may be -1 if the FileDescriptor\n> - * instance is invalid\n> - */\n> -\n> -/**\n> - * \\brief Duplicate a FileDescriptor\n> - *\n> - * Duplicating a FileDescriptor creates a duplicate of the wrapped file\n> - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function\n> - * of the original and the get() function of the duplicate will return different\n> - * values. The duplicate instance will not be affected by destruction of the\n> - * original instance or its copies.\n> - *\n> - * \\return A UniqueFD owning a duplicate of the original file descriptor\n> - */\n> -UniqueFD FileDescriptor::dup() const\n> -{\n> -\tint dupFd = ::dup(fd());\n> -\tif (dupFd == -1) {\n> -\t\tint ret = -errno;\n> -\t\tLOG(FileDescriptor, Error)\n> -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> -\t}\n> -\n> -\treturn UniqueFD(dupFd);\n> -}\n> -\n> -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)\n> -{\n> -\tif (!duplicate) {\n> -\t\tfd_ = fd;\n> -\t\treturn;\n> -\t}\n> -\n> -\t/* Failing to dup() a fd should not happen and is fatal. */\n> -\tfd_ = ::dup(fd);\n> -\tif (fd_ == -1) {\n> -\t\tint ret = -errno;\n> -\t\tLOG(FileDescriptor, Fatal)\n> -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> -\t}\n> -}\n> -\n> -FileDescriptor::Descriptor::~Descriptor()\n> -{\n> -\tif (fd_ != -1)\n> -\t\tclose(fd_);\n> -}\n> -\n> -} /* namespace libcamera */\n> diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\n> index b0d85bc19245..ccb746c27466 100644\n> --- a/src/libcamera/base/meson.build\n> +++ b/src/libcamera/base/meson.build\n> @@ -8,12 +8,12 @@ libcamera_base_sources = files([\n>      'event_dispatcher_poll.cpp',\n>      'event_notifier.cpp',\n>      'file.cpp',\n> -    'file_descriptor.cpp',\n>      'flags.cpp',\n>      'log.cpp',\n>      'message.cpp',\n>      'object.cpp',\n>      'semaphore.cpp',\n> +    'shared_fd.cpp',\n>      'signal.cpp',\n>      'thread.cpp',\n>      'timer.cpp',\n> diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp\n> new file mode 100644\n> index 000000000000..05b6892f7e19\n> --- /dev/null\n> +++ b/src/libcamera/base/shared_fd.cpp\n> @@ -0,0 +1,262 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2019, Google Inc.\n> + *\n> + * shared_fd.cpp - File descriptor wrapper with shared ownership\n> + */\n> +\n> +#include <libcamera/base/shared_fd.h>\n> +\n> +#include <string.h>\n> +#include <sys/types.h>\n> +#include <unistd.h>\n> +#include <utility>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/base/unique_fd.h>\n> +\n> +/**\n> + * \\file base/shared_fd.h\n> + * \\brief File descriptor wrapper\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(SharedFD)\n> +\n> +/**\n> + * \\class SharedFD\n> + * \\brief RAII-style wrapper for file descriptors\n> + *\n> + * The SharedFD class provides RAII-style lifetime management of file\n> + * descriptors with an efficient mechanism for ownership sharing. At its core,\n> + * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> + * integer) with an RAII-style interface. The Descriptor is then implicitly\n> + * shared with all SharedFD instances constructed as copies.\n> + *\n> + * When constructed from a numerical file descriptor, the SharedFD instance\n> + * either duplicates or takes over the file descriptor:\n> + *\n> + * - The SharedFD(const int &) constructor duplicates the numerical file\n> + *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> + *   responsible for closing the original file descriptor, and the value\n> + *   returned by fd() will be different from the value passed to the\n> + *   constructor.\n> + *\n> + * - The SharedFD(int &&) constructor takes over the numerical file descriptor\n> + *   and wraps it in a Descriptor. The caller shall not touch the original file\n> + *   descriptor once the function returns, and the value returned by fd() will\n> + *   be identical to the value passed to the constructor.\n> + *\n> + * The copy constructor and assignment operator create copies that share the\n> + * Descriptor, while the move versions of those functions additionally make the\n> + * other SharedFD invalid. When the last SharedFD that references a Descriptor\n> + * is destroyed, the file descriptor is closed.\n> + *\n> + * The numerical file descriptor is available through the fd() function. All\n> + * SharedFD instances created as copies of a SharedFD will report the same fd()\n> + * value. Callers can perform operations on the fd(), but shall never close it\n> + * manually.\n> + */\n> +\n> +/**\n> + * \\brief Create a SharedFD copying a given \\a fd\n> + * \\param[in] fd File descriptor\n> + *\n> + * Construct a SharedFD from a numerical file descriptor by duplicating the\n> + * \\a fd, and take ownership of the copy. The original \\a fd is left untouched,\n> + * and the caller is responsible for closing it when appropriate. The duplicated\n> + * file descriptor will be closed automatically when all SharedFD instances that\n> + * reference it are destroyed.\n> + *\n> + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> + * function will return -1.\n> + */\n> +SharedFD::SharedFD(const int &fd)\n> +{\n> +\tif (fd < 0)\n> +\t\treturn;\n> +\n> +\tfd_ = std::make_shared<Descriptor>(fd, true);\n> +\tif (fd_->fd() < 0)\n> +\t\tfd_.reset();\n> +}\n> +\n> +/**\n> + * \\brief Create a SharedFD taking ownership of a given \\a fd\n> + * \\param[in] fd File descriptor\n> + *\n> + * Construct a SharedFD from a numerical file descriptor by taking ownership of\n> + * the \\a fd. The original \\a fd is set to -1 and shall not be touched by the\n> + * caller anymore. In particular, the caller shall not close the original \\a fd\n> + * manually. The duplicated file descriptor will be closed automatically when\n> + * all SharedFD instances that reference it are destroyed.\n> + *\n> + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> + * function will return -1.\n> + */\n> +SharedFD::SharedFD(int &&fd)\n> +{\n> +\tif (fd < 0)\n> +\t\treturn;\n> +\n> +\tfd_ = std::make_shared<Descriptor>(fd, false);\n> +\t/*\n> +\t * The Descriptor constructor can't have failed here, as it took over\n> +\t * the fd without duplicating it. Just set the original fd to -1 to\n> +\t * implement move semantics.\n> +\t */\n> +\tfd = -1;\n> +}\n> +\n> +/**\n> + * \\brief Create a SharedFD taking ownership of a given UniqueFD \\a fd\n> + * \\param[in] fd UniqueFD\n> + *\n> + * Construct a SharedFD from UniqueFD by taking ownership of the \\a fd. The\n> + * original \\a fd becomes invalid.\n> + */\n> +SharedFD::SharedFD(UniqueFD fd)\n> +\t: SharedFD(fd.release())\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Copy constructor, create a SharedFD from a copy of \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Copying a SharedFD implicitly shares ownership of the wrapped file\n> + * descriptor. The original SharedFD is left untouched, and the caller is\n> + * responsible for destroying it when appropriate. The wrapped file descriptor\n> + * will be closed automatically when all SharedFD instances that reference it\n> + * are destroyed.\n> + */\n> +SharedFD::SharedFD(const SharedFD &other)\n> +\t: fd_(other.fd_)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Move constructor, create a SharedFD by taking over \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> + * \\a other to the new SharedFD. The \\a other SharedFD is invalidated and its\n> + * fd() function will return -1. The wrapped file descriptor will be closed\n> + * automatically when all SharedFD instances that reference it are destroyed.\n> + */\n> +SharedFD::SharedFD(SharedFD &&other)\n> +\t: fd_(std::move(other.fd_))\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Destroy the SharedFD instance\n> + *\n> + * Destroying a SharedFD instance releases its reference to the wrapped\n> + * descriptor, if any. When the last instance that references a wrapped\n> + * descriptor is destroyed, the file descriptor is automatically closed.\n> + */\n> +SharedFD::~SharedFD()\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> + * copy of \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Copying a SharedFD creates a new reference to the wrapped file descriptor\n> + * owner by \\a other. If \\a other is invalid, *this will also be invalid. The\n> + * original SharedFD is left untouched, and the caller is responsible for\n> + * destroying it when appropriate. The wrapped file descriptor will be closed\n> + * automatically when all SharedFD instances that reference it are destroyed.\n> + *\n> + * \\return A reference to this SharedFD\n> + */\n> +SharedFD &SharedFD::operator=(const SharedFD &other)\n> +{\n> +\tfd_ = other.fd_;\n> +\n> +\treturn *this;\n> +}\n> +\n> +/**\n> + * \\brief Move assignment operator, replace the wrapped file descriptor by\n> + * taking over \\a other\n> + * \\param[in] other The other SharedFD\n> + *\n> + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> + * \\a other to the new SharedFD. If \\a other is invalid, *this will also be\n> + * invalid. The \\a other SharedFD is invalidated and its fd() function will\n> + * return -1. The wrapped file descriptor will be closed automatically when\n> + * all SharedFD instances that reference it are destroyed.\n> + *\n> + * \\return A reference to this SharedFD\n> + */\n> +SharedFD &SharedFD::operator=(SharedFD &&other)\n> +{\n> +\tfd_ = std::move(other.fd_);\n> +\n> +\treturn *this;\n> +}\n> +\n> +/**\n> + * \\fn SharedFD::isValid()\n> + * \\brief Check if the SharedFD instance is valid\n> + * \\return True if the SharedFD is valid, false otherwise\n> + */\n> +\n> +/**\n> + * \\fn SharedFD::fd()\n> + * \\brief Retrieve the numerical file descriptor\n> + * \\return The numerical file descriptor, which may be -1 if the SharedFD\n> + * instance is invalid\n> + */\n> +\n> +/**\n> + * \\brief Duplicate a SharedFD\n> + *\n> + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and\n> + * returns a UniqueFD that owns the duplicate. The fd() function of the original\n> + * and the get() function of the duplicate will return different values. The\n> + * duplicate instance will not be affected by destruction of the original\n> + * instance or its copies.\n> + *\n> + * \\return A UniqueFD owning a duplicate of the original file descriptor\n> + */\n> +UniqueFD SharedFD::dup() const\n> +{\n> +\tint dupFd = ::dup(fd());\n> +\tif (dupFd == -1) {\n> +\t\tint ret = -errno;\n> +\t\tLOG(SharedFD, Error)\n> +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> +\t}\n> +\n> +\treturn UniqueFD(dupFd);\n> +}\n> +\n> +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)\n> +{\n> +\tif (!duplicate) {\n> +\t\tfd_ = fd;\n> +\t\treturn;\n> +\t}\n> +\n> +\t/* Failing to dup() a fd should not happen and is fatal. */\n> +\tfd_ = ::dup(fd);\n> +\tif (fd_ == -1) {\n> +\t\tint ret = -errno;\n> +\t\tLOG(SharedFD, Fatal)\n> +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> +\t}\n> +}\n> +\n> +SharedFD::Descriptor::~Descriptor()\n> +{\n> +\tif (fd_ != -1)\n> +\t\tclose(fd_);\n> +}\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> index f5bcf107d7aa..0a5bf7fdbeb7 100644\n> --- a/src/libcamera/framebuffer.cpp\n> +++ b/src/libcamera/framebuffer.cpp\n> @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()\n>   * offset and length.\n>   *\n>   * To support DMA access, planes are associated with dmabuf objects represented\n> - * by FileDescriptor handles. The Plane class doesn't handle mapping of the\n> - * memory to the CPU, but applications and IPAs may use the dmabuf file\n> - * descriptors to map the plane memory with mmap() and access its contents.\n> + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to\n> + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map\n> + * the plane memory with mmap() and access its contents.\n>   *\n>   * \\todo Specify how an application shall decide whether to use a single or\n>   * multiple dmabufs, based on the camera requirements.\n> diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> index 82ec9b20a411..0a259305afa2 100644\n> --- a/src/libcamera/ipa_data_serializer.cpp\n> +++ b/src/libcamera/ipa_data_serializer.cpp\n> @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)\n>   *\n>   * \\todo Harden the vector and map deserializer\n>   *\n> - * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> + * \\todo For SharedFDs, instead of storing a validity flag, store an\n>   * index into the fd array. This will allow us to use views instead of copying.\n>   */\n>\n> @@ -112,7 +112,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() can be used if the object type \\a T and its\n> - * members don't have any FileDescriptor.\n> + * members don't have any SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -132,7 +132,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() can be used if the object type \\a T and its\n> - * members don't have any FileDescriptor.\n> + * members don't have any SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -143,7 +143,7 @@ namespace {\n>  /**\n>   * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n>   * \tconst std::vector<uint8_t> &data,\n> - * \tconst std::vector<FileDescriptor> &fds,\n> + * \tconst std::vector<SharedFD> &fds,\n>   * \tControlSerializer *cs = nullptr)\n>   * \\brief Deserialize byte vector and fd vector into an object\n>   * \\tparam T Type of object to deserialize to\n> @@ -152,7 +152,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() (or the iterator version) must be used if\n> - * the object type \\a T or its members contain FileDescriptor.\n> + * the object type \\a T or its members contain SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -164,8 +164,8 @@ namespace {\n>   * \\fn template<typename T> IPADataSerializer::deserialize(\n>   * \tstd::vector<uint8_t>::const_iterator dataBegin,\n>   * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> - * \tstd::vector<FileDescriptor>::const_iterator fdsBegin,\n> - * \tstd::vector<FileDescriptor>::const_iterator fdsEnd,\n> + * \tstd::vector<SharedFD>::const_iterator fdsBegin,\n> + * \tstd::vector<SharedFD>::const_iterator fdsEnd,\n>   * \tControlSerializer *cs = nullptr)\n>   * \\brief Deserialize byte vector and fd vector into an object\n>   * \\tparam T Type of object to deserialize to\n> @@ -176,7 +176,7 @@ namespace {\n>   * \\param[in] cs ControlSerializer\n>   *\n>   * This version of deserialize() (or the vector version) must be used if\n> - * the object type \\a T or its members contain FileDescriptor.\n> + * the object type \\a T or its members contain SharedFD.\n>   *\n>   * \\a cs is only necessary if the object type \\a T or its members contain\n>   * ControlList or ControlInfoMap.\n> @@ -189,7 +189,7 @@ namespace {\n>  #define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n>  \t\t\t\t\t\t\t\t\t\\\n>  template<>\t\t\t\t\t\t\t\t\\\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\t\t\\\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\t\t\\\n>  IPADataSerializer<type>::serialize(const type &data,\t\t\t\\\n>  \t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n>  {\t\t\t\t\t\t\t\t\t\\\n> @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n>  \t\t\t\t\t\t\t\t\t\\\n>  template<>\t\t\t\t\t\t\t\t\\\n>  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> -\t\t\t\t\t  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \\\n> +\t\t\t\t\t  [[maybe_unused]] const std::vector<SharedFD> &fds, \\\n>  \t\t\t\t\t  ControlSerializer *cs)\t\\\n>  {\t\t\t\t\t\t\t\t\t\\\n>  \treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n> @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n>  template<>\t\t\t\t\t\t\t\t\\\n>  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n>  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n> -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \\\n> -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \\\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \\\n> +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \\\n>  \t\t\t\t\t  ControlSerializer *cs)\t\\\n>  {\t\t\t\t\t\t\t\t\t\\\n>  \treturn deserialize(dataBegin, dataEnd, cs);\t\t\t\\\n> @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)\n>   * function parameter serdes).\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<std::string>::serialize(const std::string &data,\n>  \t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n>  {\n> @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n>  template<>\n>  std::string\n>  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n>  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \treturn { data.cbegin(), data.cend() };\n> @@ -288,8 +288,8 @@ template<>\n>  std::string\n>  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \treturn { dataBegin, dataEnd };\n> @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n>   * be used. The serialized ControlInfoMap will have zero length.\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)\n>  {\n>  \tif (!cs)\n> @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n>  template<>\n>  ControlList\n>  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n>  \t\t\t\t\t    ControlSerializer *cs)\n>  {\n>  \treturn deserialize(data.cbegin(), data.end(), cs);\n> @@ -417,8 +417,8 @@ template<>\n>  ControlList\n>  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t    ControlSerializer *cs)\n>  {\n>  \treturn deserialize(dataBegin, dataEnd, cs);\n> @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator\n>   * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,\n>  \t\t\t\t\t     ControlSerializer *cs)\n>  {\n> @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n>  template<>\n>  ControlInfoMap\n>  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t\t\t       [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> +\t\t\t\t\t       [[maybe_unused]] const std::vector<SharedFD> &fds,\n>  \t\t\t\t\t       ControlSerializer *cs)\n>  {\n>  \treturn deserialize(data.cbegin(), data.end(), cs);\n> @@ -503,30 +503,30 @@ template<>\n>  ControlInfoMap\n>  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t       ControlSerializer *cs)\n>  {\n>  \treturn deserialize(dataBegin, dataEnd, cs);\n>  }\n>\n>  /*\n> - * FileDescriptors are serialized into four bytes that tells if the\n> - * FileDescriptor is valid or not. If it is valid, then for serialization\n> - * the fd will be written to the fd vector, or for deserialization the\n> - * fd vector const_iterator will be valid.\n> + * SharedFD instances are serialized into four bytes that tells if the SharedFD\n> + * is valid or not. If it is valid, then for serialization the fd will be\n> + * written to the fd vector, or for deserialization the fd vector const_iterator\n> + * will be valid.\n>   *\n>   * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n>   * also allows us to simply send the entire fd vector into the deserializer\n>   * and it will be recursively consumed as necessary.\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> -\t\t\t\t\t     [[maybe_unused]] ControlSerializer *cs)\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,\n> +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \tstd::vector<uint8_t> dataVec;\n> -\tstd::vector<FileDescriptor> fdVec;\n> +\tstd::vector<SharedFD> fdVec;\n>\n>  \t/*\n>  \t * Store as uint32_t to prepare for conversion from validity flag\n> @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n>  }\n>\n>  template<>\n> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> -\t\t\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsEnd,\n> -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> +\t\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsEnd,\n> +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \tASSERT(std::distance(dataBegin, dataEnd) >= 4);\n>\n> @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s\n>\n>  \tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n>\n> -\treturn valid ? *fdsBegin : FileDescriptor();\n> +\treturn valid ? *fdsBegin : SharedFD();\n>  }\n>\n>  template<>\n> -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t\t\t\t\t      const std::vector<FileDescriptor> &fds,\n> -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,\n> +\t\t\t\t\t\t  const std::vector<SharedFD> &fds,\n> +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n>  }\n> @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<\n>  /*\n>   * FrameBuffer::Plane is serialized as:\n>   *\n> - * 4 byte  - FileDescriptor\n> + * 4 byte  - SharedFD\n>   * 4 bytes - uint32_t Offset\n>   * 4 bytes - uint32_t Length\n>   */\n>  template<>\n> -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,\n>  \t\t\t\t\t\t [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \tstd::vector<uint8_t> dataVec;\n> -\tstd::vector<FileDescriptor> fdsVec;\n> +\tstd::vector<SharedFD> fdsVec;\n>\n>  \tstd::vector<uint8_t> fdBuf;\n> -\tstd::vector<FileDescriptor> fdFds;\n> +\tstd::vector<SharedFD> fdFds;\n>  \tstd::tie(fdBuf, fdFds) =\n> -\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> +\t\tIPADataSerializer<SharedFD>::serialize(data.fd);\n>  \tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n>  \tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n>\n> @@ -597,13 +597,13 @@ template<>\n>  FrameBuffer::Plane\n>  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t\t\t\t\t   std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t\t\t\t\t   std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t\t\t\t\t   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t\t\t\t\t   std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t\t\t\t\t   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t\t\t\t\t   [[maybe_unused]] ControlSerializer *cs)\n>  {\n>  \tFrameBuffer::Plane ret;\n>\n> -\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,\n> +\tret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,\n>  \t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n>  \tret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n>  \tret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);\n> @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i\n>  template<>\n>  FrameBuffer::Plane\n>  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,\n> -\t\t\t\t\t\t   const std::vector<FileDescriptor> &fds,\n> +\t\t\t\t\t\t   const std::vector<SharedFD> &fds,\n>  \t\t\t\t\t\t   ControlSerializer *cs)\n>  {\n>  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp\n> index ad870fd4137f..3b47032de0a2 100644\n> --- a/src/libcamera/ipc_pipe.cpp\n> +++ b/src/libcamera/ipc_pipe.cpp\n> @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)\n>  \tdata_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),\n>  \t\t\t\t     payload.data.end());\n>  \tfor (int32_t &fd : payload.fds)\n> -\t\tfds_.push_back(FileDescriptor(std::move(fd)));\n> +\t\tfds_.push_back(SharedFD(std::move(fd)));\n>  }\n>\n>  /**\n> @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const\n>  \t\t       data_.data(), data_.size());\n>  \t}\n>\n> -\tfor (const FileDescriptor &fd : fds_)\n> +\tfor (const SharedFD &fd : fds_)\n>  \t\tpayload.fds.push_back(fd.fd());\n>\n>  \treturn payload;\n> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> index ffa51a0c65ca..ea8243912a29 100644\n> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> @@ -12,7 +12,7 @@\n>  #include <queue>\n>  #include <unordered_set>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/control_ids.h>\n> @@ -228,7 +228,7 @@ public:\n>\n>  \t/* DMAHEAP allocation helper. */\n>  \tRPi::DmaHeap dmaHeap_;\n> -\tFileDescriptor lsTable_;\n> +\tSharedFD lsTable_;\n>\n>  \tstd::unique_ptr<DelayedControls> delayedCtrls_;\n>  \tbool sensorMetadata_;\n> @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n>  \t\tif (!fd.isValid())\n>  \t\t\treturn -ENOMEM;\n>\n> -\t\tlsTable_ = FileDescriptor(std::move(fd));\n> +\t\tlsTable_ = SharedFD(std::move(fd));\n>\n>  \t\t/* Allow the IPA to mmap the LS table via the file descriptor. */\n>  \t\t/*\n> diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> index 3966483a365f..97d431071def 100644\n> --- a/src/libcamera/v4l2_videodevice.cpp\n> +++ b/src/libcamera/v4l2_videodevice.cpp\n> @@ -22,8 +22,8 @@\n>  #include <linux/version.h>\n>\n>  #include <libcamera/base/event_notifier.h>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/log.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/unique_fd.h>\n>  #include <libcamera/base/utils.h>\n>\n> @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>  \t\t\treturn nullptr;\n>\n>  \t\tFrameBuffer::Plane plane;\n> -\t\tplane.fd = FileDescriptor(std::move(fd));\n> +\t\tplane.fd = SharedFD(std::move(fd));\n>  \t\t/*\n>  \t\t * V4L2 API doesn't provide dmabuf offset information of plane.\n>  \t\t * Set 0 as a placeholder offset.\n> @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n>  \t\tASSERT(numPlanes == 1u);\n>\n>  \t\tplanes.resize(formatInfo_->numPlanes());\n> -\t\tconst FileDescriptor &fd = planes[0].fd;\n> +\t\tconst SharedFD &fd = planes[0].fd;\n>  \t\tsize_t offset = 0;\n>\n>  \t\tfor (auto [i, plane] : utils::enumerate(planes)) {\n> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> index 9817fd393d59..586347829845 100644\n> --- a/src/v4l2/v4l2_camera.h\n> +++ b/src/v4l2/v4l2_camera.h\n> @@ -11,8 +11,8 @@\n>  #include <mutex>\n>  #include <utility>\n>\n> -#include <libcamera/base/file_descriptor.h>\n>  #include <libcamera/base/semaphore.h>\n> +#include <libcamera/base/shared_fd.h>\n>\n>  #include <libcamera/camera.h>\n>  #include <libcamera/framebuffer.h>\n> diff --git a/test/meson.build b/test/meson.build\n> index 42dfbc1f8ee9..daaa3862cdd6 100644\n> --- a/test/meson.build\n> +++ b/test/meson.build\n> @@ -40,7 +40,6 @@ internal_tests = [\n>      ['event-dispatcher',                'event-dispatcher.cpp'],\n>      ['event-thread',                    'event-thread.cpp'],\n>      ['file',                            'file.cpp'],\n> -    ['file-descriptor',                 'file-descriptor.cpp'],\n>      ['flags',                           'flags.cpp'],\n>      ['hotplug-cameras',                 'hotplug-cameras.cpp'],\n>      ['mapped-buffer',                   'mapped-buffer.cpp'],\n> @@ -49,6 +48,7 @@ internal_tests = [\n>      ['object-delete',                   'object-delete.cpp'],\n>      ['object-invoke',                   'object-invoke.cpp'],\n>      ['pixel-format',                    'pixel-format.cpp'],\n> +    ['shared-fd',                       'shared-fd.cpp'],\n>      ['signal-threads',                  'signal-threads.cpp'],\n>      ['threads',                         'threads.cpp'],\n>      ['timer',                           'timer.cpp'],\n> diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp\n> index 5fcdcb8eae92..d2050a868b38 100644\n> --- a/test/serialization/ipa_data_serializer_test.cpp\n> +++ b/test/serialization/ipa_data_serializer_test.cpp\n> @@ -53,7 +53,7 @@ template<typename T>\n>  int testPodSerdes(T in)\n>  {\n>  \tstd::vector<uint8_t> buf;\n> -\tstd::vector<FileDescriptor> fds;\n> +\tstd::vector<SharedFD> fds;\n>\n>  \tstd::tie(buf, fds) = IPADataSerializer<T>::serialize(in);\n>  \tT out = IPADataSerializer<T>::deserialize(buf, fds);\n> @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,\n>  \t\t     ControlSerializer *cs = nullptr)\n>  {\n>  \tstd::vector<uint8_t> buf;\n> -\tstd::vector<FileDescriptor> fds;\n> +\tstd::vector<SharedFD> fds;\n>\n>  \tstd::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);\n>  \tstd::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);\n> @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,\n>  \t\t  ControlSerializer *cs = nullptr)\n>  {\n>  \tstd::vector<uint8_t> buf;\n> -\tstd::vector<FileDescriptor> fds;\n> +\tstd::vector<SharedFD> fds;\n>\n>  \tstd::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);\n>  \tstd::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);\n> @@ -198,7 +198,7 @@ private:\n>  \t\tControlSerializer cs(ControlSerializer::Role::Proxy);\n>\n>  \t\t/*\n> -\t\t * We don't test FileDescriptor serdes because it dup()s, so we\n> +\t\t * We don't test SharedFD serdes because it dup()s, so we\n>  \t\t * can't check for equality.\n>  \t\t */\n>  \t\tstd::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };\n> @@ -219,7 +219,7 @@ private:\n>  \t\t};\n>\n>  \t\tstd::vector<uint8_t> buf;\n> -\t\tstd::vector<FileDescriptor> fds;\n> +\t\tstd::vector<SharedFD> fds;\n>\n>  \t\tif (testVectorSerdes(vecUint8) != TestPass)\n>  \t\t\treturn TestFail;\n> @@ -291,7 +291,7 @@ private:\n>  \t\t\t{ { \"a\", { 1, 2, 3 } }, { \"b\", { 4, 5, 6 } }, { \"c\", { 7, 8, 9 } } };\n>\n>  \t\tstd::vector<uint8_t> buf;\n> -\t\tstd::vector<FileDescriptor> fds;\n> +\t\tstd::vector<SharedFD> fds;\n>\n>  \t\tif (testMapSerdes(mapUintStr) != TestPass)\n>  \t\t\treturn TestFail;\n> @@ -359,7 +359,7 @@ private:\n>  \t\tstd::string strEmpty = \"\";\n>\n>  \t\tstd::vector<uint8_t> buf;\n> -\t\tstd::vector<FileDescriptor> fds;\n> +\t\tstd::vector<SharedFD> fds;\n>\n>  \t\tif (testPodSerdes(u32min) != TestPass)\n>  \t\t\treturn TestFail;\n> diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp\n> similarity index 80%\n> rename from test/file-descriptor.cpp\n> rename to test/shared-fd.cpp\n> index 76badc4c5fad..60e5d0aaa395 100644\n> --- a/test/file-descriptor.cpp\n> +++ b/test/shared-fd.cpp\n> @@ -2,7 +2,7 @@\n>  /*\n>   * Copyright (C) 2019, Google Inc.\n>   *\n> - * file_descriptor.cpp - FileDescriptor test\n> + * shared_fd.cpp - SharedFD test\n>   */\n>\n>  #include <fcntl.h>\n> @@ -11,7 +11,7 @@\n>  #include <sys/types.h>\n>  #include <unistd.h>\n>\n> -#include <libcamera/base/file_descriptor.h>\n> +#include <libcamera/base/shared_fd.h>\n>  #include <libcamera/base/utils.h>\n>\n>  #include \"test.h\"\n> @@ -19,7 +19,7 @@\n>  using namespace libcamera;\n>  using namespace std;\n>\n> -class FileDescriptorTest : public Test\n> +class SharedFDTest : public Test\n>  {\n>  protected:\n>  \tint init()\n> @@ -43,8 +43,8 @@ protected:\n>\n>  \tint run()\n>  \t{\n> -\t\t/* Test creating empty FileDescriptor. */\n> -\t\tdesc1_ = new FileDescriptor();\n> +\t\t/* Test creating empty SharedFD. */\n> +\t\tdesc1_ = new SharedFD();\n>\n>  \t\tif (desc1_->fd() != -1) {\n>  \t\t\tstd::cout << \"Failed fd numerical check (default constructor)\"\n> @@ -56,10 +56,10 @@ protected:\n>  \t\tdesc1_ = nullptr;\n>\n>  \t\t/*\n> -\t\t * Test creating FileDescriptor by copying numerical file\n> +\t\t * Test creating SharedFD by copying numerical file\n>  \t\t * descriptor.\n>  \t\t */\n> -\t\tdesc1_ = new FileDescriptor(fd_);\n> +\t\tdesc1_ = new SharedFD(fd_);\n>  \t\tif (desc1_->fd() == fd_) {\n>  \t\t\tstd::cout << \"Failed fd numerical check (lvalue ref constructor)\"\n>  \t\t\t\t  << std::endl;\n> @@ -84,13 +84,13 @@ protected:\n>  \t\t}\n>\n>  \t\t/*\n> -\t\t * Test creating FileDescriptor by taking ownership of\n> +\t\t * Test creating SharedFD by taking ownership of\n>  \t\t * numerical file descriptor.\n>  \t\t */\n>  \t\tint dupFd = dup(fd_);\n>  \t\tint dupFdCopy = dupFd;\n>\n> -\t\tdesc1_ = new FileDescriptor(std::move(dupFd));\n> +\t\tdesc1_ = new SharedFD(std::move(dupFd));\n>  \t\tif (desc1_->fd() != dupFdCopy) {\n>  \t\t\tstd::cout << \"Failed fd numerical check (rvalue ref constructor)\"\n>  \t\t\t\t  << std::endl;\n> @@ -114,9 +114,9 @@ protected:\n>  \t\t\treturn TestFail;\n>  \t\t}\n>\n> -\t\t/* Test creating FileDescriptor from other FileDescriptor. */\n> -\t\tdesc1_ = new FileDescriptor(fd_);\n> -\t\tdesc2_ = new FileDescriptor(*desc1_);\n> +\t\t/* Test creating SharedFD from other SharedFD. */\n> +\t\tdesc1_ = new SharedFD(fd_);\n> +\t\tdesc2_ = new SharedFD(*desc1_);\n>\n>  \t\tif (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {\n>  \t\t\tstd::cout << \"Failed fd numerical check (copy constructor)\"\n> @@ -142,10 +142,10 @@ protected:\n>  \t\tdelete desc2_;\n>  \t\tdesc2_ = nullptr;\n>\n> -\t\t/* Test creating FileDescriptor by taking over other FileDescriptor. */\n> -\t\tdesc1_ = new FileDescriptor(fd_);\n> +\t\t/* Test creating SharedFD by taking over other SharedFD. */\n> +\t\tdesc1_ = new SharedFD(fd_);\n>  \t\tfd = desc1_->fd();\n> -\t\tdesc2_ = new FileDescriptor(std::move(*desc1_));\n> +\t\tdesc2_ = new SharedFD(std::move(*desc1_));\n>\n>  \t\tif (desc1_->fd() != -1 || desc2_->fd() != fd) {\n>  \t\t\tstd::cout << \"Failed fd numerical check (move constructor)\"\n> @@ -164,9 +164,9 @@ protected:\n>  \t\tdelete desc2_;\n>  \t\tdesc2_ = nullptr;\n>\n> -\t\t/* Test creating FileDescriptor by copy assignment. */\n> -\t\tdesc1_ = new FileDescriptor();\n> -\t\tdesc2_ = new FileDescriptor(fd_);\n> +\t\t/* Test creating SharedFD by copy assignment. */\n> +\t\tdesc1_ = new SharedFD();\n> +\t\tdesc2_ = new SharedFD(fd_);\n>\n>  \t\tfd = desc2_->fd();\n>  \t\t*desc1_ = *desc2_;\n> @@ -188,9 +188,9 @@ protected:\n>  \t\tdelete desc2_;\n>  \t\tdesc2_ = nullptr;\n>\n> -\t\t/* Test creating FileDescriptor by move assignment. */\n> -\t\tdesc1_ = new FileDescriptor();\n> -\t\tdesc2_ = new FileDescriptor(fd_);\n> +\t\t/* Test creating SharedFD by move assignment. */\n> +\t\tdesc1_ = new SharedFD();\n> +\t\tdesc2_ = new SharedFD(fd_);\n>\n>  \t\tfd = desc2_->fd();\n>  \t\t*desc1_ = std::move(*desc2_);\n> @@ -237,7 +237,7 @@ private:\n>\n>  \tint fd_;\n>  \tino_t inodeNr_;\n> -\tFileDescriptor *desc1_, *desc2_;\n> +\tSharedFD *desc1_, *desc2_;\n>  };\n>\n> -TEST_REGISTER(FileDescriptorTest)\n> +TEST_REGISTER(SharedFDTest)\n> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> index d856339aa9ee..c37c4941b528 100644\n> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n>  void {{proxy_name}}::{{method.mojom_name}}IPC(\n>  \tstd::vector<uint8_t>::const_iterator data,\n>  \tsize_t dataSize,\n> -\t[[maybe_unused]] const std::vector<FileDescriptor> &fds)\n> +\t[[maybe_unused]] const std::vector<SharedFD> &fds)\n>  {\n>  {%- for param in method.parameters %}\n>  \t{{param|name}} {{param.mojom_name}};\n> diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> index ce396c183d0c..c308dd10c7e5 100644\n> --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> @@ -64,7 +64,7 @@ private:\n>  \tvoid {{method.mojom_name}}IPC(\n>  \t\tstd::vector<uint8_t>::const_iterator data,\n>  \t\tsize_t dataSize,\n> -\t\tconst std::vector<FileDescriptor> &fds);\n> +\t\tconst std::vector<SharedFD> &fds);\n>  {% endfor %}\n>\n>  \t/* Helper class to invoke async functions in another thread. */\n> diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> index ebcd2aaaafae..bac826a74c2d 100644\n> --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> @@ -54,7 +54,7 @@\n>  {%- for param in params %}\n>  \tstd::vector<uint8_t> {{param.mojom_name}}Buf;\n>  {%- if param|has_fd %}\n> -\tstd::vector<FileDescriptor> {{param.mojom_name}}Fds;\n> +\tstd::vector<SharedFD> {{param.mojom_name}}Fds;\n>  \tstd::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =\n>  {%- else %}\n>  \tstd::tie({{param.mojom_name}}Buf, std::ignore) =\n> diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> index b8ef8e7b974e..77bae36fe6b7 100644\n> --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> @@ -40,7 +40,7 @@\n>  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n>  {%- elif field|is_fd %}\n>  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n> -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n>  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n>  \t\t\tIPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});\n>  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> @@ -58,7 +58,7 @@\n>  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}\n>  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n>  \t{%- if field|has_fd %}\n> -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n>  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n>  \t{%- else %}\n>  \t\tstd::tie({{field.mojom_name}}, std::ignore) =\n> @@ -177,7 +177,7 @@\n>   # \\a struct.\n>   #}\n>  {%- macro serializer(struct, namespace) %}\n> -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n>  \tserialize(const {{struct|name_full}} &data,\n>  {%- if struct|needs_control_serializer %}\n>  \t\t  ControlSerializer *cs)\n> @@ -187,7 +187,7 @@\n>  \t{\n>  \t\tstd::vector<uint8_t> retData;\n>  {%- if struct|has_fd %}\n> -\t\tstd::vector<FileDescriptor> retFds;\n> +\t\tstd::vector<SharedFD> retFds;\n>  {%- endif %}\n>  {%- for field in struct.fields %}\n>  {{serializer_field(field, namespace, loop)}}\n> @@ -210,7 +210,7 @@\n>  {%- macro deserializer_fd(struct, namespace) %}\n>  \tstatic {{struct|name_full}}\n>  \tdeserialize(std::vector<uint8_t> &data,\n> -\t\t    std::vector<FileDescriptor> &fds,\n> +\t\t    std::vector<SharedFD> &fds,\n>  {%- if struct|needs_control_serializer %}\n>  \t\t    ControlSerializer *cs)\n>  {%- else %}\n> @@ -224,8 +224,8 @@\n>  \tstatic {{struct|name_full}}\n>  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t    std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t    std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t    std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t    std::vector<SharedFD>::const_iterator fdsEnd,\n>  {%- if struct|needs_control_serializer %}\n>  \t\t    ControlSerializer *cs)\n>  {%- else %}\n> @@ -234,7 +234,7 @@\n>  \t{\n>  \t\t{{struct|name_full}} ret;\n>  \t\tstd::vector<uint8_t>::const_iterator m = dataBegin;\n> -\t\tstd::vector<FileDescriptor>::const_iterator n = fdsBegin;\n> +\t\tstd::vector<SharedFD>::const_iterator n = fdsBegin;\n>\n>  \t\tsize_t dataSize = std::distance(dataBegin, dataEnd);\n>  \t\t[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);\n> @@ -255,7 +255,7 @@\n>  {%- macro deserializer_fd_simple(struct, namespace) %}\n>  \tstatic {{struct|name_full}}\n>  \tdeserialize(std::vector<uint8_t> &data,\n> -\t\t    [[maybe_unused]] std::vector<FileDescriptor> &fds,\n> +\t\t    [[maybe_unused]] std::vector<SharedFD> &fds,\n>  \t\t    ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);\n> @@ -264,8 +264,8 @@\n>  \tstatic {{struct|name_full}}\n>  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n>  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n>  \t\t    ControlSerializer *cs = nullptr)\n>  \t{\n>  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);\n> diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py\n> index c609f4e5c062..753bfc734e56 100644\n> --- a/utils/ipc/generators/mojom_libcamera_generator.py\n> +++ b/utils/ipc/generators/mojom_libcamera_generator.py\n> @@ -77,7 +77,7 @@ def GetDefaultValue(element):\n>      if mojom.IsEnumKind(element.kind):\n>          return f'static_cast<{element.kind.mojom_name}>(0)'\n>      if isinstance(element.kind, mojom.Struct) and \\\n> -       element.kind.mojom_name == 'FileDescriptor':\n> +       element.kind.mojom_name == 'SharedFD':\n>          return '-1'\n>      return ''\n>\n> @@ -140,7 +140,7 @@ def HasFd(element):\n>          types = GetAllTypes(element)\n>      else:\n>          types = GetAllTypes(element.kind)\n> -    return \"FileDescriptor\" in types or (attrs is not None and \"hasFd\" in attrs)\n> +    return \"SharedFD\" in types or (attrs is not None and \"hasFd\" in attrs)\n>\n>  def WithDefaultValues(element):\n>      return [x for x in element if HasDefaultValue(x)]\n> @@ -221,7 +221,7 @@ def IsEnum(element):\n>      return mojom.IsEnumKind(element.kind)\n>\n>  def IsFd(element):\n> -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"FileDescriptor\"\n> +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"SharedFD\"\n>\n>  def IsMap(element):\n>      return mojom.IsMapKind(element.kind)\n> --\n> Regards,\n>\n> Laurent Pinchart\n>","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 9B4E4BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Nov 2021 16:00:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 61690605A6;\n\tMon, 29 Nov 2021 17:00:05 +0100 (CET)","from relay10.mail.gandi.net (relay10.mail.gandi.net\n\t[217.70.178.230])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id F1FF460592\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 17:00:03 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby relay10.mail.gandi.net (Postfix) with ESMTPSA id A3061240007;\n\tMon, 29 Nov 2021 16:00:01 +0000 (UTC)"],"Date":"Mon, 29 Nov 2021 17:00:55 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20211129160055.p74ha6iidz7gxxqm@uno.localdomain>","References":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>\n\t<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":21367,"web_url":"https://patchwork.libcamera.org/comment/21367/","msgid":"<YaUM35YDv12OEerK@pendragon.ideasonboard.com>","date":"2021-11-29T17:24:47","subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Mon, Nov 29, 2021 at 05:00:55PM +0100, Jacopo Mondi wrote:\n> On Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:\n> > Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.\n> > Rename it to SharedFD.\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> makes sense!\n\nDoes this mean Rb ? :-)\n\n> > ---\n> >  include/libcamera/base/file.h                 |   4 +-\n> >  include/libcamera/base/meson.build            |   2 +-\n> >  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-\n> >  include/libcamera/framebuffer.h               |   4 +-\n> >  .../libcamera/internal/ipa_data_serializer.h  |  40 +--\n> >  include/libcamera/internal/ipc_pipe.h         |   8 +-\n> >  include/libcamera/ipa/core.mojom              |   6 +-\n> >  include/libcamera/ipa/raspberrypi.mojom       |   2 +-\n> >  src/android/camera_device.cpp                 |   2 +-\n> >  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-\n> >  src/libcamera/base/file.cpp                   |   6 +-\n> >  src/libcamera/base/file_descriptor.cpp        | 266 ------------------\n> >  src/libcamera/base/meson.build                |   2 +-\n> >  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++\n> >  src/libcamera/framebuffer.cpp                 |   6 +-\n> >  src/libcamera/ipa_data_serializer.cpp         | 100 +++----\n> >  src/libcamera/ipc_pipe.cpp                    |   4 +-\n> >  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-\n> >  src/libcamera/v4l2_videodevice.cpp            |   6 +-\n> >  src/v4l2/v4l2_camera.h                        |   2 +-\n> >  test/meson.build                              |   2 +-\n> >  .../ipa_data_serializer_test.cpp              |  14 +-\n> >  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--\n> >  .../module_ipa_proxy.cpp.tmpl                 |   2 +-\n> >  .../module_ipa_proxy.h.tmpl                   |   2 +-\n> >  .../libcamera_templates/proxy_functions.tmpl  |   2 +-\n> >  .../libcamera_templates/serializer.tmpl       |  22 +-\n> >  .../generators/mojom_libcamera_generator.py   |   6 +-\n> >  28 files changed, 422 insertions(+), 426 deletions(-)\n> >  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)\n> >  delete mode 100644 src/libcamera/base/file_descriptor.cpp\n> >  create mode 100644 src/libcamera/base/shared_fd.cpp\n> >  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)\n> >\n> > diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h\n> > index 47769da7abc2..691b52d6ab2d 100644\n> > --- a/include/libcamera/base/file.h\n> > +++ b/include/libcamera/base/file.h\n> > @@ -21,7 +21,7 @@\n> >\n> >  namespace libcamera {\n> >\n> > -class FileDescriptor;\n> > +class SharedFD;\n> >\n> >  class File\n> >  {\n> > @@ -69,7 +69,7 @@ public:\n> >  \tbool unmap(uint8_t *addr);\n> >\n> >  \tstatic bool exists(const std::string &name);\n> > -\tstatic ino_t inode(const FileDescriptor &fd);\n> > +\tstatic ino_t inode(const SharedFD &fd);\n> >\n> >  private:\n> >  \tLIBCAMERA_DISABLE_COPY(File)\n> > diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\n> > index cca374a769cc..112420dab225 100644\n> > --- a/include/libcamera/base/meson.build\n> > +++ b/include/libcamera/base/meson.build\n> > @@ -11,13 +11,13 @@ libcamera_base_headers = files([\n> >      'event_dispatcher_poll.h',\n> >      'event_notifier.h',\n> >      'file.h',\n> > -    'file_descriptor.h',\n> >      'flags.h',\n> >      'log.h',\n> >      'message.h',\n> >      'object.h',\n> >      'private.h',\n> >      'semaphore.h',\n> > +    'shared_fd.h',\n> >      'signal.h',\n> >      'span.h',\n> >      'thread.h',\n> > diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h\n> > similarity index 55%\n> > rename from include/libcamera/base/file_descriptor.h\n> > rename to include/libcamera/base/shared_fd.h\n> > index 12a43f95d414..a786885ceb32 100644\n> > --- a/include/libcamera/base/file_descriptor.h\n> > +++ b/include/libcamera/base/shared_fd.h\n> > @@ -2,7 +2,7 @@\n> >  /*\n> >   * Copyright (C) 2019, Google Inc.\n> >   *\n> > - * file_descriptor.h - File descriptor wrapper\n> > + * shared_fd.h - File descriptor wrapper with shared ownership\n> >   */\n> >\n> >  #pragma once\n> > @@ -13,18 +13,18 @@ namespace libcamera {\n> >\n> >  class UniqueFD;\n> >\n> > -class FileDescriptor final\n> > +class SharedFD final\n> >  {\n> >  public:\n> > -\texplicit FileDescriptor(const int &fd = -1);\n> > -\texplicit FileDescriptor(int &&fd);\n> > -\texplicit FileDescriptor(UniqueFD fd);\n> > -\tFileDescriptor(const FileDescriptor &other);\n> > -\tFileDescriptor(FileDescriptor &&other);\n> > -\t~FileDescriptor();\n> > +\texplicit SharedFD(const int &fd = -1);\n> > +\texplicit SharedFD(int &&fd);\n> > +\texplicit SharedFD(UniqueFD fd);\n> > +\tSharedFD(const SharedFD &other);\n> > +\tSharedFD(SharedFD &&other);\n> > +\t~SharedFD();\n> >\n> > -\tFileDescriptor &operator=(const FileDescriptor &other);\n> > -\tFileDescriptor &operator=(FileDescriptor &&other);\n> > +\tSharedFD &operator=(const SharedFD &other);\n> > +\tSharedFD &operator=(SharedFD &&other);\n> >\n> >  \tbool isValid() const { return fd_ != nullptr; }\n> >  \tint fd() const { return fd_ ? fd_->fd() : -1; }\n> > diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> > index 2fbea9c5be16..357bbe189551 100644\n> > --- a/include/libcamera/framebuffer.h\n> > +++ b/include/libcamera/framebuffer.h\n> > @@ -13,7 +13,7 @@\n> >  #include <vector>\n> >\n> >  #include <libcamera/base/class.h>\n> > -#include <libcamera/base/file_descriptor.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >  #include <libcamera/base/span.h>\n> >\n> >  namespace libcamera {\n> > @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible\n> >  public:\n> >  \tstruct Plane {\n> >  \t\tstatic constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();\n> > -\t\tFileDescriptor fd;\n> > +\t\tSharedFD fd;\n> >  \t\tunsigned int offset = kInvalidOffset;\n> >  \t\tunsigned int length;\n> >  \t};\n> > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\n> > index c2f602d5b7de..a87449c9be48 100644\n> > --- a/include/libcamera/internal/ipa_data_serializer.h\n> > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > @@ -66,7 +66,7 @@ template<typename T>\n> >  class IPADataSerializer\n> >  {\n> >  public:\n> > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  \tserialize(const T &data, ControlSerializer *cs = nullptr);\n> >\n> >  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> > @@ -76,12 +76,12 @@ public:\n> >  \t\t\t     ControlSerializer *cs = nullptr);\n> >\n> >  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t     const std::vector<FileDescriptor> &fds,\n> > +\t\t\t     const std::vector<SharedFD> &fds,\n> >  \t\t\t     ControlSerializer *cs = nullptr);\n> >  \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t     std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t     std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t     ControlSerializer *cs = nullptr);\n> >  };\n> >\n> > @@ -104,11 +104,11 @@ template<typename V>\n> >  class IPADataSerializer<std::vector<V>>\n> >  {\n> >  public:\n> > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  \tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\tstd::vector<uint8_t> dataVec;\n> > -\t\tstd::vector<FileDescriptor> fdsVec;\n> > +\t\tstd::vector<SharedFD> fdsVec;\n> >\n> >  \t\t/* Serialize the length. */\n> >  \t\tuint32_t vecLen = data.size();\n> > @@ -117,7 +117,7 @@ public:\n> >  \t\t/* Serialize the members. */\n> >  \t\tfor (auto const &it : data) {\n> >  \t\t\tstd::vector<uint8_t> dvec;\n> > -\t\t\tstd::vector<FileDescriptor> fvec;\n> > +\t\t\tstd::vector<SharedFD> fvec;\n> >\n> >  \t\t\tstd::tie(dvec, fvec) =\n> >  \t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > @@ -141,11 +141,11 @@ public:\n> >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> > -\t\tstd::vector<FileDescriptor> fds;\n> > +\t\tstd::vector<SharedFD> fds;\n> >  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> >  \t}\n> >\n> > -\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > @@ -153,15 +153,15 @@ public:\n> >\n> >  \tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> >  \t\tstd::vector<V> ret(vecLen);\n> >\n> >  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> > -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> > +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n> >  \t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> >  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> >  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > @@ -201,11 +201,11 @@ template<typename K, typename V>\n> >  class IPADataSerializer<std::map<K, V>>\n> >  {\n> >  public:\n> > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  \tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\tstd::vector<uint8_t> dataVec;\n> > -\t\tstd::vector<FileDescriptor> fdsVec;\n> > +\t\tstd::vector<SharedFD> fdsVec;\n> >\n> >  \t\t/* Serialize the length. */\n> >  \t\tuint32_t mapLen = data.size();\n> > @@ -214,7 +214,7 @@ public:\n> >  \t\t/* Serialize the members. */\n> >  \t\tfor (auto const &it : data) {\n> >  \t\t\tstd::vector<uint8_t> dvec;\n> > -\t\t\tstd::vector<FileDescriptor> fvec;\n> > +\t\t\tstd::vector<SharedFD> fvec;\n> >\n> >  \t\t\tstd::tie(dvec, fvec) =\n> >  \t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > @@ -247,11 +247,11 @@ public:\n> >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> > -\t\tstd::vector<FileDescriptor> fds;\n> > +\t\tstd::vector<SharedFD> fds;\n> >  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> >  \t}\n> >\n> > -\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > @@ -259,8 +259,8 @@ public:\n> >\n> >  \tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\tstd::map<K, V> ret;\n> > @@ -268,7 +268,7 @@ public:\n> >  \t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> >\n> >  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> > -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> > +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n> >  \t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> >  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> >  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h\n> > index 986f8d886fa6..ab5dd67c3813 100644\n> > --- a/include/libcamera/internal/ipc_pipe.h\n> > +++ b/include/libcamera/internal/ipc_pipe.h\n> > @@ -9,7 +9,7 @@\n> >\n> >  #include <vector>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >  #include <libcamera/base/signal.h>\n> >\n> >  #include \"libcamera/internal/ipc_unixsocket.h\"\n> > @@ -33,17 +33,17 @@ public:\n> >\n> >  \tHeader &header() { return header_; }\n> >  \tstd::vector<uint8_t> &data() { return data_; }\n> > -\tstd::vector<FileDescriptor> &fds() { return fds_; }\n> > +\tstd::vector<SharedFD> &fds() { return fds_; }\n> >\n> >  \tconst Header &header() const { return header_; }\n> >  \tconst std::vector<uint8_t> &data() const { return data_; }\n> > -\tconst std::vector<FileDescriptor> &fds() const { return fds_; }\n> > +\tconst std::vector<SharedFD> &fds() const { return fds_; }\n> >\n> >  private:\n> >  \tHeader header_;\n> >\n> >  \tstd::vector<uint8_t> data_;\n> > -\tstd::vector<FileDescriptor> fds_;\n> > +\tstd::vector<SharedFD> fds_;\n> >  };\n> >\n> >  class IPCPipe\n> > diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom\n> > index f7eff0c7ab8c..74f3339e56f2 100644\n> > --- a/include/libcamera/ipa/core.mojom\n> > +++ b/include/libcamera/ipa/core.mojom\n> > @@ -32,7 +32,7 @@ module libcamera;\n> >   *   - This attribute instructs the build system that a (de)serializer is\n> >   *     available for the type and there's no need to generate one\n> >   * - hasFd - struct fields or empty structs only\n> > - *   - Designate that this field or empty struct contains a FileDescriptor\n> > + *   - Designate that this field or empty struct contains a SharedFD\n> >   *\n> >   * Rules:\n> >   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is\n> > @@ -60,7 +60,7 @@ module libcamera;\n> >   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array\n> >   *       member\n> >   * - [skipHeader] and [skipSerdes] only work here in core.mojom\n> > - * - If a field in a struct has a FileDescriptor, but is not explicitly\n> > + * - If a field in a struct has a SharedFD, but is not explicitly\n> >   *   defined so in mojom, then the field must be marked with the [hasFd]\n> >   *   attribute\n> >   *\n> > @@ -71,7 +71,7 @@ module libcamera;\n> >   */\n> >  [skipSerdes, skipHeader] struct ControlInfoMap {};\n> >  [skipSerdes, skipHeader] struct ControlList {};\n> > -[skipSerdes, skipHeader] struct FileDescriptor {};\n> > +[skipSerdes, skipHeader] struct SharedFD {};\n> >\n> >  [skipHeader] struct Point {\n> >  \tint32 x;\n> > diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> > index e453d46cb14f..acd3cafe6c91 100644\n> > --- a/include/libcamera/ipa/raspberrypi.mojom\n> > +++ b/include/libcamera/ipa/raspberrypi.mojom\n> > @@ -35,7 +35,7 @@ struct ISPConfig {\n> >\n> >  struct IPAConfig {\n> >  \tuint32 transform;\n> > -\tlibcamera.FileDescriptor lsTableHandle;\n> > +\tlibcamera.SharedFD lsTableHandle;\n> >  };\n> >\n> >  struct StartConfig {\n> > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\n> > index f2e0bdbdbbf6..1938b10509fa 100644\n> > --- a/src/android/camera_device.cpp\n> > +++ b/src/android/camera_device.cpp\n> > @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n> >\n> >  \tstd::vector<FrameBuffer::Plane> planes(buf.numPlanes());\n> >  \tfor (size_t i = 0; i < buf.numPlanes(); ++i) {\n> > -\t\tFileDescriptor fd{ camera3buffer->data[i] };\n> > +\t\tSharedFD fd{ camera3buffer->data[i] };\n> >  \t\tif (!fd.isValid()) {\n> >  \t\t\tLOG(HAL, Fatal) << \"No valid fd\";\n> >  \t\t\treturn nullptr;\n> > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> > index c6aec09046f7..aaf629eeb3fc 100644\n> > --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> > +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> > @@ -15,8 +15,8 @@\n> >\n> >  #include <linux/bcm2835-isp.h>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> >  #include <libcamera/base/log.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >  #include <libcamera/base/span.h>\n> >\n> >  #include <libcamera/control_ids.h>\n> > @@ -164,7 +164,7 @@ private:\n> >  \tbool processPending_;\n> >\n> >  \t/* LS table allocation passed in from the pipeline handler. */\n> > -\tFileDescriptor lsTableHandle_;\n> > +\tSharedFD lsTableHandle_;\n> >  \tvoid *lsTable_;\n> >\n> >  \t/* Distinguish the first camera start from others. */\n> > diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp\n> > index 66c73c406198..3ca9839bb989 100644\n> > --- a/src/libcamera/base/file.cpp\n> > +++ b/src/libcamera/base/file.cpp\n> > @@ -14,8 +14,8 @@\n> >  #include <sys/types.h>\n> >  #include <unistd.h>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> >  #include <libcamera/base/log.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >\n> >  /**\n> >   * \\file base/file.h\n> > @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)\n> >  }\n> >\n> >  /**\n> > - * \\brief Retrieve the inode of a FileDescriptor\n> > + * \\brief Retrieve the inode of a SharedFD\n> >   *\n> >   * \\return The file descriptor inode on success, or 0 on error\n> >   */\n> > -ino_t File::inode(const FileDescriptor &fd)\n> > +ino_t File::inode(const SharedFD &fd)\n> >  {\n> >  \tif (!fd.isValid())\n> >  \t\treturn 0;\n> > diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp\n> > deleted file mode 100644\n> > index a83bf52c31e6..000000000000\n> > --- a/src/libcamera/base/file_descriptor.cpp\n> > +++ /dev/null\n> > @@ -1,266 +0,0 @@\n> > -/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > -/*\n> > - * Copyright (C) 2019, Google Inc.\n> > - *\n> > - * file_descriptor.cpp - File descriptor wrapper\n> > - */\n> > -\n> > -#include <libcamera/base/file_descriptor.h>\n> > -\n> > -#include <string.h>\n> > -#include <sys/types.h>\n> > -#include <unistd.h>\n> > -#include <utility>\n> > -\n> > -#include <libcamera/base/log.h>\n> > -#include <libcamera/base/unique_fd.h>\n> > -\n> > -/**\n> > - * \\file base/file_descriptor.h\n> > - * \\brief File descriptor wrapper\n> > - */\n> > -\n> > -namespace libcamera {\n> > -\n> > -LOG_DEFINE_CATEGORY(FileDescriptor)\n> > -\n> > -/**\n> > - * \\class FileDescriptor\n> > - * \\brief RAII-style wrapper for file descriptors\n> > - *\n> > - * The FileDescriptor class provides RAII-style lifetime management of file\n> > - * descriptors with an efficient mechanism for ownership sharing. At its core,\n> > - * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> > - * integer) with an RAII-style interface. The Descriptor is then implicitly\n> > - * shared with all FileDescriptor instances constructed as copies.\n> > - *\n> > - * When constructed from a numerical file descriptor, the FileDescriptor\n> > - * instance either duplicates or takes over the file descriptor:\n> > - *\n> > - * - The FileDescriptor(const int &) constructor duplicates the numerical file\n> > - *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> > - *   responsible for closing the original file descriptor, and the value\n> > - *   returned by fd() will be different from the value passed to the\n> > - *   constructor.\n> > - *\n> > - * - The FileDescriptor(int &&) constructor takes over the numerical file\n> > - *   descriptor and wraps it in a Descriptor. The caller shall not touch the\n> > - *   original file descriptor once the function returns, and the value returned\n> > - *   by fd() will be identical to the value passed to the constructor.\n> > - *\n> > - * The copy constructor and assignment operator create copies that share the\n> > - * Descriptor, while the move versions of those functions additionally make the\n> > - * other FileDescriptor invalid. When the last FileDescriptor that references a\n> > - * Descriptor is destroyed, the file descriptor is closed.\n> > - *\n> > - * The numerical file descriptor is available through the fd() function. All\n> > - * FileDescriptor instances created as copies of a FileDescriptor will report\n> > - * the same fd() value. Callers can perform operations on the fd(), but shall\n> > - * never close it manually.\n> > - */\n> > -\n> > -/**\n> > - * \\brief Create a FileDescriptor copying a given \\a fd\n> > - * \\param[in] fd File descriptor\n> > - *\n> > - * Construct a FileDescriptor from a numerical file descriptor by duplicating\n> > - * the \\a fd, and take ownership of the copy. The original \\a fd is left\n> > - * untouched, and the caller is responsible for closing it when appropriate.\n> > - * The duplicated file descriptor will be closed automatically when all\n> > - * FileDescriptor instances that reference it are destroyed.\n> > - *\n> > - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> > - * the fd() function will return -1.\n> > - */\n> > -FileDescriptor::FileDescriptor(const int &fd)\n> > -{\n> > -\tif (fd < 0)\n> > -\t\treturn;\n> > -\n> > -\tfd_ = std::make_shared<Descriptor>(fd, true);\n> > -\tif (fd_->fd() < 0)\n> > -\t\tfd_.reset();\n> > -}\n> > -\n> > -/**\n> > - * \\brief Create a FileDescriptor taking ownership of a given \\a fd\n> > - * \\param[in] fd File descriptor\n> > - *\n> > - * Construct a FileDescriptor from a numerical file descriptor by taking\n> > - * ownership of the \\a fd. The original \\a fd is set to -1 and shall not be\n> > - * touched by the caller anymore. In particular, the caller shall not close the\n> > - * original \\a fd manually. The duplicated file descriptor will be closed\n> > - * automatically when all FileDescriptor instances that reference it are\n> > - * destroyed.\n> > - *\n> > - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> > - * the fd() function will return -1.\n> > - */\n> > -FileDescriptor::FileDescriptor(int &&fd)\n> > -{\n> > -\tif (fd < 0)\n> > -\t\treturn;\n> > -\n> > -\tfd_ = std::make_shared<Descriptor>(fd, false);\n> > -\t/*\n> > -\t * The Descriptor constructor can't have failed here, as it took over\n> > -\t * the fd without duplicating it. Just set the original fd to -1 to\n> > -\t * implement move semantics.\n> > -\t */\n> > -\tfd = -1;\n> > -}\n> > -\n> > -/**\n> > - * \\brief Create a FileDescriptor taking ownership of a given UniqueFD \\a fd\n> > - * \\param[in] fd UniqueFD\n> > - *\n> > - * Construct a FileDescriptor from UniqueFD by taking ownership of the \\a fd.\n> > - * The original \\a fd becomes invalid.\n> > - */\n> > -FileDescriptor::FileDescriptor(UniqueFD fd)\n> > -\t: FileDescriptor(fd.release())\n> > -{\n> > -}\n> > -\n> > -/**\n> > - * \\brief Copy constructor, create a FileDescriptor from a copy of \\a other\n> > - * \\param[in] other The other FileDescriptor\n> > - *\n> > - * Copying a FileDescriptor implicitly shares ownership of the wrapped file\n> > - * descriptor. The original FileDescriptor is left untouched, and the caller is\n> > - * responsible for destroying it when appropriate. The wrapped file descriptor\n> > - * will be closed automatically when all FileDescriptor instances that\n> > - * reference it are destroyed.\n> > - */\n> > -FileDescriptor::FileDescriptor(const FileDescriptor &other)\n> > -\t: fd_(other.fd_)\n> > -{\n> > -}\n> > -\n> > -/**\n> > - * \\brief Move constructor, create a FileDescriptor by taking over \\a other\n> > - * \\param[in] other The other FileDescriptor\n> > - *\n> > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> > - * by \\a other to the new FileDescriptor. The \\a other FileDescriptor is\n> > - * invalidated and its fd() function will return -1. The wrapped file descriptor\n> > - * will be closed automatically when all FileDescriptor instances that\n> > - * reference it are destroyed.\n> > - */\n> > -FileDescriptor::FileDescriptor(FileDescriptor &&other)\n> > -\t: fd_(std::move(other.fd_))\n> > -{\n> > -}\n> > -\n> > -/**\n> > - * \\brief Destroy the FileDescriptor instance\n> > - *\n> > - * Destroying a FileDescriptor instance releases its reference to the wrapped\n> > - * descriptor, if any. When the last instance that references a wrapped\n> > - * descriptor is destroyed, the file descriptor is automatically closed.\n> > - */\n> > -FileDescriptor::~FileDescriptor()\n> > -{\n> > -}\n> > -\n> > -/**\n> > - * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> > - * copy of \\a other\n> > - * \\param[in] other The other FileDescriptor\n> > - *\n> > - * Copying a FileDescriptor creates a new reference to the wrapped file\n> > - * descriptor owner by \\a other. If \\a other is invalid, *this will also be\n> > - * invalid. The original FileDescriptor is left untouched, and the caller is\n> > - * responsible for destroying it when appropriate. The wrapped file descriptor\n> > - * will be closed automatically when all FileDescriptor instances that\n> > - * reference it are destroyed.\n> > - *\n> > - * \\return A reference to this FileDescriptor\n> > - */\n> > -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)\n> > -{\n> > -\tfd_ = other.fd_;\n> > -\n> > -\treturn *this;\n> > -}\n> > -\n> > -/**\n> > - * \\brief Move assignment operator, replace the wrapped file descriptor by\n> > - * taking over \\a other\n> > - * \\param[in] other The other FileDescriptor\n> > - *\n> > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> > - * by \\a other to the new FileDescriptor. If \\a other is invalid, *this will\n> > - * also be invalid. The \\a other FileDescriptor is invalidated and its fd()\n> > - * function will return -1. The wrapped file descriptor will be closed\n> > - * automatically when all FileDescriptor instances that reference it are\n> > - * destroyed.\n> > - *\n> > - * \\return A reference to this FileDescriptor\n> > - */\n> > -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)\n> > -{\n> > -\tfd_ = std::move(other.fd_);\n> > -\n> > -\treturn *this;\n> > -}\n> > -\n> > -/**\n> > - * \\fn FileDescriptor::isValid()\n> > - * \\brief Check if the FileDescriptor instance is valid\n> > - * \\return True if the FileDescriptor is valid, false otherwise\n> > - */\n> > -\n> > -/**\n> > - * \\fn FileDescriptor::fd()\n> > - * \\brief Retrieve the numerical file descriptor\n> > - * \\return The numerical file descriptor, which may be -1 if the FileDescriptor\n> > - * instance is invalid\n> > - */\n> > -\n> > -/**\n> > - * \\brief Duplicate a FileDescriptor\n> > - *\n> > - * Duplicating a FileDescriptor creates a duplicate of the wrapped file\n> > - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function\n> > - * of the original and the get() function of the duplicate will return different\n> > - * values. The duplicate instance will not be affected by destruction of the\n> > - * original instance or its copies.\n> > - *\n> > - * \\return A UniqueFD owning a duplicate of the original file descriptor\n> > - */\n> > -UniqueFD FileDescriptor::dup() const\n> > -{\n> > -\tint dupFd = ::dup(fd());\n> > -\tif (dupFd == -1) {\n> > -\t\tint ret = -errno;\n> > -\t\tLOG(FileDescriptor, Error)\n> > -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > -\t}\n> > -\n> > -\treturn UniqueFD(dupFd);\n> > -}\n> > -\n> > -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)\n> > -{\n> > -\tif (!duplicate) {\n> > -\t\tfd_ = fd;\n> > -\t\treturn;\n> > -\t}\n> > -\n> > -\t/* Failing to dup() a fd should not happen and is fatal. */\n> > -\tfd_ = ::dup(fd);\n> > -\tif (fd_ == -1) {\n> > -\t\tint ret = -errno;\n> > -\t\tLOG(FileDescriptor, Fatal)\n> > -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > -\t}\n> > -}\n> > -\n> > -FileDescriptor::Descriptor::~Descriptor()\n> > -{\n> > -\tif (fd_ != -1)\n> > -\t\tclose(fd_);\n> > -}\n> > -\n> > -} /* namespace libcamera */\n> > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\n> > index b0d85bc19245..ccb746c27466 100644\n> > --- a/src/libcamera/base/meson.build\n> > +++ b/src/libcamera/base/meson.build\n> > @@ -8,12 +8,12 @@ libcamera_base_sources = files([\n> >      'event_dispatcher_poll.cpp',\n> >      'event_notifier.cpp',\n> >      'file.cpp',\n> > -    'file_descriptor.cpp',\n> >      'flags.cpp',\n> >      'log.cpp',\n> >      'message.cpp',\n> >      'object.cpp',\n> >      'semaphore.cpp',\n> > +    'shared_fd.cpp',\n> >      'signal.cpp',\n> >      'thread.cpp',\n> >      'timer.cpp',\n> > diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp\n> > new file mode 100644\n> > index 000000000000..05b6892f7e19\n> > --- /dev/null\n> > +++ b/src/libcamera/base/shared_fd.cpp\n> > @@ -0,0 +1,262 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2019, Google Inc.\n> > + *\n> > + * shared_fd.cpp - File descriptor wrapper with shared ownership\n> > + */\n> > +\n> > +#include <libcamera/base/shared_fd.h>\n> > +\n> > +#include <string.h>\n> > +#include <sys/types.h>\n> > +#include <unistd.h>\n> > +#include <utility>\n> > +\n> > +#include <libcamera/base/log.h>\n> > +#include <libcamera/base/unique_fd.h>\n> > +\n> > +/**\n> > + * \\file base/shared_fd.h\n> > + * \\brief File descriptor wrapper\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DEFINE_CATEGORY(SharedFD)\n> > +\n> > +/**\n> > + * \\class SharedFD\n> > + * \\brief RAII-style wrapper for file descriptors\n> > + *\n> > + * The SharedFD class provides RAII-style lifetime management of file\n> > + * descriptors with an efficient mechanism for ownership sharing. At its core,\n> > + * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> > + * integer) with an RAII-style interface. The Descriptor is then implicitly\n> > + * shared with all SharedFD instances constructed as copies.\n> > + *\n> > + * When constructed from a numerical file descriptor, the SharedFD instance\n> > + * either duplicates or takes over the file descriptor:\n> > + *\n> > + * - The SharedFD(const int &) constructor duplicates the numerical file\n> > + *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> > + *   responsible for closing the original file descriptor, and the value\n> > + *   returned by fd() will be different from the value passed to the\n> > + *   constructor.\n> > + *\n> > + * - The SharedFD(int &&) constructor takes over the numerical file descriptor\n> > + *   and wraps it in a Descriptor. The caller shall not touch the original file\n> > + *   descriptor once the function returns, and the value returned by fd() will\n> > + *   be identical to the value passed to the constructor.\n> > + *\n> > + * The copy constructor and assignment operator create copies that share the\n> > + * Descriptor, while the move versions of those functions additionally make the\n> > + * other SharedFD invalid. When the last SharedFD that references a Descriptor\n> > + * is destroyed, the file descriptor is closed.\n> > + *\n> > + * The numerical file descriptor is available through the fd() function. All\n> > + * SharedFD instances created as copies of a SharedFD will report the same fd()\n> > + * value. Callers can perform operations on the fd(), but shall never close it\n> > + * manually.\n> > + */\n> > +\n> > +/**\n> > + * \\brief Create a SharedFD copying a given \\a fd\n> > + * \\param[in] fd File descriptor\n> > + *\n> > + * Construct a SharedFD from a numerical file descriptor by duplicating the\n> > + * \\a fd, and take ownership of the copy. The original \\a fd is left untouched,\n> > + * and the caller is responsible for closing it when appropriate. The duplicated\n> > + * file descriptor will be closed automatically when all SharedFD instances that\n> > + * reference it are destroyed.\n> > + *\n> > + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> > + * function will return -1.\n> > + */\n> > +SharedFD::SharedFD(const int &fd)\n> > +{\n> > +\tif (fd < 0)\n> > +\t\treturn;\n> > +\n> > +\tfd_ = std::make_shared<Descriptor>(fd, true);\n> > +\tif (fd_->fd() < 0)\n> > +\t\tfd_.reset();\n> > +}\n> > +\n> > +/**\n> > + * \\brief Create a SharedFD taking ownership of a given \\a fd\n> > + * \\param[in] fd File descriptor\n> > + *\n> > + * Construct a SharedFD from a numerical file descriptor by taking ownership of\n> > + * the \\a fd. The original \\a fd is set to -1 and shall not be touched by the\n> > + * caller anymore. In particular, the caller shall not close the original \\a fd\n> > + * manually. The duplicated file descriptor will be closed automatically when\n> > + * all SharedFD instances that reference it are destroyed.\n> > + *\n> > + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> > + * function will return -1.\n> > + */\n> > +SharedFD::SharedFD(int &&fd)\n> > +{\n> > +\tif (fd < 0)\n> > +\t\treturn;\n> > +\n> > +\tfd_ = std::make_shared<Descriptor>(fd, false);\n> > +\t/*\n> > +\t * The Descriptor constructor can't have failed here, as it took over\n> > +\t * the fd without duplicating it. Just set the original fd to -1 to\n> > +\t * implement move semantics.\n> > +\t */\n> > +\tfd = -1;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Create a SharedFD taking ownership of a given UniqueFD \\a fd\n> > + * \\param[in] fd UniqueFD\n> > + *\n> > + * Construct a SharedFD from UniqueFD by taking ownership of the \\a fd. The\n> > + * original \\a fd becomes invalid.\n> > + */\n> > +SharedFD::SharedFD(UniqueFD fd)\n> > +\t: SharedFD(fd.release())\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\brief Copy constructor, create a SharedFD from a copy of \\a other\n> > + * \\param[in] other The other SharedFD\n> > + *\n> > + * Copying a SharedFD implicitly shares ownership of the wrapped file\n> > + * descriptor. The original SharedFD is left untouched, and the caller is\n> > + * responsible for destroying it when appropriate. The wrapped file descriptor\n> > + * will be closed automatically when all SharedFD instances that reference it\n> > + * are destroyed.\n> > + */\n> > +SharedFD::SharedFD(const SharedFD &other)\n> > +\t: fd_(other.fd_)\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\brief Move constructor, create a SharedFD by taking over \\a other\n> > + * \\param[in] other The other SharedFD\n> > + *\n> > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> > + * \\a other to the new SharedFD. The \\a other SharedFD is invalidated and its\n> > + * fd() function will return -1. The wrapped file descriptor will be closed\n> > + * automatically when all SharedFD instances that reference it are destroyed.\n> > + */\n> > +SharedFD::SharedFD(SharedFD &&other)\n> > +\t: fd_(std::move(other.fd_))\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\brief Destroy the SharedFD instance\n> > + *\n> > + * Destroying a SharedFD instance releases its reference to the wrapped\n> > + * descriptor, if any. When the last instance that references a wrapped\n> > + * descriptor is destroyed, the file descriptor is automatically closed.\n> > + */\n> > +SharedFD::~SharedFD()\n> > +{\n> > +}\n> > +\n> > +/**\n> > + * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> > + * copy of \\a other\n> > + * \\param[in] other The other SharedFD\n> > + *\n> > + * Copying a SharedFD creates a new reference to the wrapped file descriptor\n> > + * owner by \\a other. If \\a other is invalid, *this will also be invalid. The\n> > + * original SharedFD is left untouched, and the caller is responsible for\n> > + * destroying it when appropriate. The wrapped file descriptor will be closed\n> > + * automatically when all SharedFD instances that reference it are destroyed.\n> > + *\n> > + * \\return A reference to this SharedFD\n> > + */\n> > +SharedFD &SharedFD::operator=(const SharedFD &other)\n> > +{\n> > +\tfd_ = other.fd_;\n> > +\n> > +\treturn *this;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Move assignment operator, replace the wrapped file descriptor by\n> > + * taking over \\a other\n> > + * \\param[in] other The other SharedFD\n> > + *\n> > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> > + * \\a other to the new SharedFD. If \\a other is invalid, *this will also be\n> > + * invalid. The \\a other SharedFD is invalidated and its fd() function will\n> > + * return -1. The wrapped file descriptor will be closed automatically when\n> > + * all SharedFD instances that reference it are destroyed.\n> > + *\n> > + * \\return A reference to this SharedFD\n> > + */\n> > +SharedFD &SharedFD::operator=(SharedFD &&other)\n> > +{\n> > +\tfd_ = std::move(other.fd_);\n> > +\n> > +\treturn *this;\n> > +}\n> > +\n> > +/**\n> > + * \\fn SharedFD::isValid()\n> > + * \\brief Check if the SharedFD instance is valid\n> > + * \\return True if the SharedFD is valid, false otherwise\n> > + */\n> > +\n> > +/**\n> > + * \\fn SharedFD::fd()\n> > + * \\brief Retrieve the numerical file descriptor\n> > + * \\return The numerical file descriptor, which may be -1 if the SharedFD\n> > + * instance is invalid\n> > + */\n> > +\n> > +/**\n> > + * \\brief Duplicate a SharedFD\n> > + *\n> > + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and\n> > + * returns a UniqueFD that owns the duplicate. The fd() function of the original\n> > + * and the get() function of the duplicate will return different values. The\n> > + * duplicate instance will not be affected by destruction of the original\n> > + * instance or its copies.\n> > + *\n> > + * \\return A UniqueFD owning a duplicate of the original file descriptor\n> > + */\n> > +UniqueFD SharedFD::dup() const\n> > +{\n> > +\tint dupFd = ::dup(fd());\n> > +\tif (dupFd == -1) {\n> > +\t\tint ret = -errno;\n> > +\t\tLOG(SharedFD, Error)\n> > +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > +\t}\n> > +\n> > +\treturn UniqueFD(dupFd);\n> > +}\n> > +\n> > +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)\n> > +{\n> > +\tif (!duplicate) {\n> > +\t\tfd_ = fd;\n> > +\t\treturn;\n> > +\t}\n> > +\n> > +\t/* Failing to dup() a fd should not happen and is fatal. */\n> > +\tfd_ = ::dup(fd);\n> > +\tif (fd_ == -1) {\n> > +\t\tint ret = -errno;\n> > +\t\tLOG(SharedFD, Fatal)\n> > +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > +\t}\n> > +}\n> > +\n> > +SharedFD::Descriptor::~Descriptor()\n> > +{\n> > +\tif (fd_ != -1)\n> > +\t\tclose(fd_);\n> > +}\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > index f5bcf107d7aa..0a5bf7fdbeb7 100644\n> > --- a/src/libcamera/framebuffer.cpp\n> > +++ b/src/libcamera/framebuffer.cpp\n> > @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()\n> >   * offset and length.\n> >   *\n> >   * To support DMA access, planes are associated with dmabuf objects represented\n> > - * by FileDescriptor handles. The Plane class doesn't handle mapping of the\n> > - * memory to the CPU, but applications and IPAs may use the dmabuf file\n> > - * descriptors to map the plane memory with mmap() and access its contents.\n> > + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to\n> > + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map\n> > + * the plane memory with mmap() and access its contents.\n> >   *\n> >   * \\todo Specify how an application shall decide whether to use a single or\n> >   * multiple dmabufs, based on the camera requirements.\n> > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> > index 82ec9b20a411..0a259305afa2 100644\n> > --- a/src/libcamera/ipa_data_serializer.cpp\n> > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)\n> >   *\n> >   * \\todo Harden the vector and map deserializer\n> >   *\n> > - * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> > + * \\todo For SharedFDs, instead of storing a validity flag, store an\n> >   * index into the fd array. This will allow us to use views instead of copying.\n> >   */\n> >\n> > @@ -112,7 +112,7 @@ namespace {\n> >   * \\param[in] cs ControlSerializer\n> >   *\n> >   * This version of deserialize() can be used if the object type \\a T and its\n> > - * members don't have any FileDescriptor.\n> > + * members don't have any SharedFD.\n> >   *\n> >   * \\a cs is only necessary if the object type \\a T or its members contain\n> >   * ControlList or ControlInfoMap.\n> > @@ -132,7 +132,7 @@ namespace {\n> >   * \\param[in] cs ControlSerializer\n> >   *\n> >   * This version of deserialize() can be used if the object type \\a T and its\n> > - * members don't have any FileDescriptor.\n> > + * members don't have any SharedFD.\n> >   *\n> >   * \\a cs is only necessary if the object type \\a T or its members contain\n> >   * ControlList or ControlInfoMap.\n> > @@ -143,7 +143,7 @@ namespace {\n> >  /**\n> >   * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> >   * \tconst std::vector<uint8_t> &data,\n> > - * \tconst std::vector<FileDescriptor> &fds,\n> > + * \tconst std::vector<SharedFD> &fds,\n> >   * \tControlSerializer *cs = nullptr)\n> >   * \\brief Deserialize byte vector and fd vector into an object\n> >   * \\tparam T Type of object to deserialize to\n> > @@ -152,7 +152,7 @@ namespace {\n> >   * \\param[in] cs ControlSerializer\n> >   *\n> >   * This version of deserialize() (or the iterator version) must be used if\n> > - * the object type \\a T or its members contain FileDescriptor.\n> > + * the object type \\a T or its members contain SharedFD.\n> >   *\n> >   * \\a cs is only necessary if the object type \\a T or its members contain\n> >   * ControlList or ControlInfoMap.\n> > @@ -164,8 +164,8 @@ namespace {\n> >   * \\fn template<typename T> IPADataSerializer::deserialize(\n> >   * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> >   * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> > - * \tstd::vector<FileDescriptor>::const_iterator fdsBegin,\n> > - * \tstd::vector<FileDescriptor>::const_iterator fdsEnd,\n> > + * \tstd::vector<SharedFD>::const_iterator fdsBegin,\n> > + * \tstd::vector<SharedFD>::const_iterator fdsEnd,\n> >   * \tControlSerializer *cs = nullptr)\n> >   * \\brief Deserialize byte vector and fd vector into an object\n> >   * \\tparam T Type of object to deserialize to\n> > @@ -176,7 +176,7 @@ namespace {\n> >   * \\param[in] cs ControlSerializer\n> >   *\n> >   * This version of deserialize() (or the vector version) must be used if\n> > - * the object type \\a T or its members contain FileDescriptor.\n> > + * the object type \\a T or its members contain SharedFD.\n> >   *\n> >   * \\a cs is only necessary if the object type \\a T or its members contain\n> >   * ControlList or ControlInfoMap.\n> > @@ -189,7 +189,7 @@ namespace {\n> >  #define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n> >  \t\t\t\t\t\t\t\t\t\\\n> >  template<>\t\t\t\t\t\t\t\t\\\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\t\t\\\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\t\t\\\n> >  IPADataSerializer<type>::serialize(const type &data,\t\t\t\\\n> >  \t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n> >  {\t\t\t\t\t\t\t\t\t\\\n> > @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> >  \t\t\t\t\t\t\t\t\t\\\n> >  template<>\t\t\t\t\t\t\t\t\\\n> >  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> > -\t\t\t\t\t  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \\\n> > +\t\t\t\t\t  [[maybe_unused]] const std::vector<SharedFD> &fds, \\\n> >  \t\t\t\t\t  ControlSerializer *cs)\t\\\n> >  {\t\t\t\t\t\t\t\t\t\\\n> >  \treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n> > @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> >  template<>\t\t\t\t\t\t\t\t\\\n> >  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n> >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n> > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \\\n> > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \\\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \\\n> > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \\\n> >  \t\t\t\t\t  ControlSerializer *cs)\t\\\n> >  {\t\t\t\t\t\t\t\t\t\\\n> >  \treturn deserialize(dataBegin, dataEnd, cs);\t\t\t\\\n> > @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)\n> >   * function parameter serdes).\n> >   */\n> >  template<>\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  IPADataSerializer<std::string>::serialize(const std::string &data,\n> >  \t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> > @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n> >  template<>\n> >  std::string\n> >  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \treturn { data.cbegin(), data.cend() };\n> > @@ -288,8 +288,8 @@ template<>\n> >  std::string\n> >  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \treturn { dataBegin, dataEnd };\n> > @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n> >   * be used. The serialized ControlInfoMap will have zero length.\n> >   */\n> >  template<>\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)\n> >  {\n> >  \tif (!cs)\n> > @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> >  template<>\n> >  ControlList\n> >  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t    ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(data.cbegin(), data.end(), cs);\n> > @@ -417,8 +417,8 @@ template<>\n> >  ControlList\n> >  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t    ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(dataBegin, dataEnd, cs);\n> > @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator\n> >   * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> >   */\n> >  template<>\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,\n> >  \t\t\t\t\t     ControlSerializer *cs)\n> >  {\n> > @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> >  template<>\n> >  ControlInfoMap\n> >  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t\t\t       [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > +\t\t\t\t\t       [[maybe_unused]] const std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t       ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(data.cbegin(), data.end(), cs);\n> > @@ -503,30 +503,30 @@ template<>\n> >  ControlInfoMap\n> >  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t       ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(dataBegin, dataEnd, cs);\n> >  }\n> >\n> >  /*\n> > - * FileDescriptors are serialized into four bytes that tells if the\n> > - * FileDescriptor is valid or not. If it is valid, then for serialization\n> > - * the fd will be written to the fd vector, or for deserialization the\n> > - * fd vector const_iterator will be valid.\n> > + * SharedFD instances are serialized into four bytes that tells if the SharedFD\n> > + * is valid or not. If it is valid, then for serialization the fd will be\n> > + * written to the fd vector, or for deserialization the fd vector const_iterator\n> > + * will be valid.\n> >   *\n> >   * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n> >   * also allows us to simply send the entire fd vector into the deserializer\n> >   * and it will be recursively consumed as necessary.\n> >   */\n> >  template<>\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> > -\t\t\t\t\t     [[maybe_unused]] ControlSerializer *cs)\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,\n> > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \tstd::vector<uint8_t> dataVec;\n> > -\tstd::vector<FileDescriptor> fdVec;\n> > +\tstd::vector<SharedFD> fdVec;\n> >\n> >  \t/*\n> >  \t * Store as uint32_t to prepare for conversion from validity flag\n> > @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> >  }\n> >\n> >  template<>\n> > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> > -\t\t\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> > +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> > +\t\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> > +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsEnd,\n> > +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \tASSERT(std::distance(dataBegin, dataEnd) >= 4);\n> >\n> > @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s\n> >\n> >  \tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n> >\n> > -\treturn valid ? *fdsBegin : FileDescriptor();\n> > +\treturn valid ? *fdsBegin : SharedFD();\n> >  }\n> >\n> >  template<>\n> > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t\t\t\t\t      const std::vector<FileDescriptor> &fds,\n> > -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> > +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,\n> > +\t\t\t\t\t\t  const std::vector<SharedFD> &fds,\n> > +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n> >  }\n> > @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<\n> >  /*\n> >   * FrameBuffer::Plane is serialized as:\n> >   *\n> > - * 4 byte  - FileDescriptor\n> > + * 4 byte  - SharedFD\n> >   * 4 bytes - uint32_t Offset\n> >   * 4 bytes - uint32_t Length\n> >   */\n> >  template<>\n> > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,\n> >  \t\t\t\t\t\t [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \tstd::vector<uint8_t> dataVec;\n> > -\tstd::vector<FileDescriptor> fdsVec;\n> > +\tstd::vector<SharedFD> fdsVec;\n> >\n> >  \tstd::vector<uint8_t> fdBuf;\n> > -\tstd::vector<FileDescriptor> fdFds;\n> > +\tstd::vector<SharedFD> fdFds;\n> >  \tstd::tie(fdBuf, fdFds) =\n> > -\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > +\t\tIPADataSerializer<SharedFD>::serialize(data.fd);\n> >  \tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> >  \tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> >\n> > @@ -597,13 +597,13 @@ template<>\n> >  FrameBuffer::Plane\n> >  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t\t\t\t\t   std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t\t\t\t\t   std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t\t\t\t\t   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t\t\t\t\t   std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t\t\t\t\t   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t\t\t\t\t   [[maybe_unused]] ControlSerializer *cs)\n> >  {\n> >  \tFrameBuffer::Plane ret;\n> >\n> > -\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,\n> > +\tret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,\n> >  \t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> >  \tret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> >  \tret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);\n> > @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i\n> >  template<>\n> >  FrameBuffer::Plane\n> >  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,\n> > -\t\t\t\t\t\t   const std::vector<FileDescriptor> &fds,\n> > +\t\t\t\t\t\t   const std::vector<SharedFD> &fds,\n> >  \t\t\t\t\t\t   ControlSerializer *cs)\n> >  {\n> >  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp\n> > index ad870fd4137f..3b47032de0a2 100644\n> > --- a/src/libcamera/ipc_pipe.cpp\n> > +++ b/src/libcamera/ipc_pipe.cpp\n> > @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)\n> >  \tdata_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),\n> >  \t\t\t\t     payload.data.end());\n> >  \tfor (int32_t &fd : payload.fds)\n> > -\t\tfds_.push_back(FileDescriptor(std::move(fd)));\n> > +\t\tfds_.push_back(SharedFD(std::move(fd)));\n> >  }\n> >\n> >  /**\n> > @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const\n> >  \t\t       data_.data(), data_.size());\n> >  \t}\n> >\n> > -\tfor (const FileDescriptor &fd : fds_)\n> > +\tfor (const SharedFD &fd : fds_)\n> >  \t\tpayload.fds.push_back(fd.fd());\n> >\n> >  \treturn payload;\n> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > index ffa51a0c65ca..ea8243912a29 100644\n> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > @@ -12,7 +12,7 @@\n> >  #include <queue>\n> >  #include <unordered_set>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >\n> >  #include <libcamera/camera.h>\n> >  #include <libcamera/control_ids.h>\n> > @@ -228,7 +228,7 @@ public:\n> >\n> >  \t/* DMAHEAP allocation helper. */\n> >  \tRPi::DmaHeap dmaHeap_;\n> > -\tFileDescriptor lsTable_;\n> > +\tSharedFD lsTable_;\n> >\n> >  \tstd::unique_ptr<DelayedControls> delayedCtrls_;\n> >  \tbool sensorMetadata_;\n> > @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n> >  \t\tif (!fd.isValid())\n> >  \t\t\treturn -ENOMEM;\n> >\n> > -\t\tlsTable_ = FileDescriptor(std::move(fd));\n> > +\t\tlsTable_ = SharedFD(std::move(fd));\n> >\n> >  \t\t/* Allow the IPA to mmap the LS table via the file descriptor. */\n> >  \t\t/*\n> > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > index 3966483a365f..97d431071def 100644\n> > --- a/src/libcamera/v4l2_videodevice.cpp\n> > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > @@ -22,8 +22,8 @@\n> >  #include <linux/version.h>\n> >\n> >  #include <libcamera/base/event_notifier.h>\n> > -#include <libcamera/base/file_descriptor.h>\n> >  #include <libcamera/base/log.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >  #include <libcamera/base/unique_fd.h>\n> >  #include <libcamera/base/utils.h>\n> >\n> > @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> >  \t\t\treturn nullptr;\n> >\n> >  \t\tFrameBuffer::Plane plane;\n> > -\t\tplane.fd = FileDescriptor(std::move(fd));\n> > +\t\tplane.fd = SharedFD(std::move(fd));\n> >  \t\t/*\n> >  \t\t * V4L2 API doesn't provide dmabuf offset information of plane.\n> >  \t\t * Set 0 as a placeholder offset.\n> > @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> >  \t\tASSERT(numPlanes == 1u);\n> >\n> >  \t\tplanes.resize(formatInfo_->numPlanes());\n> > -\t\tconst FileDescriptor &fd = planes[0].fd;\n> > +\t\tconst SharedFD &fd = planes[0].fd;\n> >  \t\tsize_t offset = 0;\n> >\n> >  \t\tfor (auto [i, plane] : utils::enumerate(planes)) {\n> > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> > index 9817fd393d59..586347829845 100644\n> > --- a/src/v4l2/v4l2_camera.h\n> > +++ b/src/v4l2/v4l2_camera.h\n> > @@ -11,8 +11,8 @@\n> >  #include <mutex>\n> >  #include <utility>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> >  #include <libcamera/base/semaphore.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >\n> >  #include <libcamera/camera.h>\n> >  #include <libcamera/framebuffer.h>\n> > diff --git a/test/meson.build b/test/meson.build\n> > index 42dfbc1f8ee9..daaa3862cdd6 100644\n> > --- a/test/meson.build\n> > +++ b/test/meson.build\n> > @@ -40,7 +40,6 @@ internal_tests = [\n> >      ['event-dispatcher',                'event-dispatcher.cpp'],\n> >      ['event-thread',                    'event-thread.cpp'],\n> >      ['file',                            'file.cpp'],\n> > -    ['file-descriptor',                 'file-descriptor.cpp'],\n> >      ['flags',                           'flags.cpp'],\n> >      ['hotplug-cameras',                 'hotplug-cameras.cpp'],\n> >      ['mapped-buffer',                   'mapped-buffer.cpp'],\n> > @@ -49,6 +48,7 @@ internal_tests = [\n> >      ['object-delete',                   'object-delete.cpp'],\n> >      ['object-invoke',                   'object-invoke.cpp'],\n> >      ['pixel-format',                    'pixel-format.cpp'],\n> > +    ['shared-fd',                       'shared-fd.cpp'],\n> >      ['signal-threads',                  'signal-threads.cpp'],\n> >      ['threads',                         'threads.cpp'],\n> >      ['timer',                           'timer.cpp'],\n> > diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp\n> > index 5fcdcb8eae92..d2050a868b38 100644\n> > --- a/test/serialization/ipa_data_serializer_test.cpp\n> > +++ b/test/serialization/ipa_data_serializer_test.cpp\n> > @@ -53,7 +53,7 @@ template<typename T>\n> >  int testPodSerdes(T in)\n> >  {\n> >  \tstd::vector<uint8_t> buf;\n> > -\tstd::vector<FileDescriptor> fds;\n> > +\tstd::vector<SharedFD> fds;\n> >\n> >  \tstd::tie(buf, fds) = IPADataSerializer<T>::serialize(in);\n> >  \tT out = IPADataSerializer<T>::deserialize(buf, fds);\n> > @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,\n> >  \t\t     ControlSerializer *cs = nullptr)\n> >  {\n> >  \tstd::vector<uint8_t> buf;\n> > -\tstd::vector<FileDescriptor> fds;\n> > +\tstd::vector<SharedFD> fds;\n> >\n> >  \tstd::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);\n> >  \tstd::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);\n> > @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,\n> >  \t\t  ControlSerializer *cs = nullptr)\n> >  {\n> >  \tstd::vector<uint8_t> buf;\n> > -\tstd::vector<FileDescriptor> fds;\n> > +\tstd::vector<SharedFD> fds;\n> >\n> >  \tstd::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);\n> >  \tstd::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);\n> > @@ -198,7 +198,7 @@ private:\n> >  \t\tControlSerializer cs(ControlSerializer::Role::Proxy);\n> >\n> >  \t\t/*\n> > -\t\t * We don't test FileDescriptor serdes because it dup()s, so we\n> > +\t\t * We don't test SharedFD serdes because it dup()s, so we\n> >  \t\t * can't check for equality.\n> >  \t\t */\n> >  \t\tstd::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };\n> > @@ -219,7 +219,7 @@ private:\n> >  \t\t};\n> >\n> >  \t\tstd::vector<uint8_t> buf;\n> > -\t\tstd::vector<FileDescriptor> fds;\n> > +\t\tstd::vector<SharedFD> fds;\n> >\n> >  \t\tif (testVectorSerdes(vecUint8) != TestPass)\n> >  \t\t\treturn TestFail;\n> > @@ -291,7 +291,7 @@ private:\n> >  \t\t\t{ { \"a\", { 1, 2, 3 } }, { \"b\", { 4, 5, 6 } }, { \"c\", { 7, 8, 9 } } };\n> >\n> >  \t\tstd::vector<uint8_t> buf;\n> > -\t\tstd::vector<FileDescriptor> fds;\n> > +\t\tstd::vector<SharedFD> fds;\n> >\n> >  \t\tif (testMapSerdes(mapUintStr) != TestPass)\n> >  \t\t\treturn TestFail;\n> > @@ -359,7 +359,7 @@ private:\n> >  \t\tstd::string strEmpty = \"\";\n> >\n> >  \t\tstd::vector<uint8_t> buf;\n> > -\t\tstd::vector<FileDescriptor> fds;\n> > +\t\tstd::vector<SharedFD> fds;\n> >\n> >  \t\tif (testPodSerdes(u32min) != TestPass)\n> >  \t\t\treturn TestFail;\n> > diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp\n> > similarity index 80%\n> > rename from test/file-descriptor.cpp\n> > rename to test/shared-fd.cpp\n> > index 76badc4c5fad..60e5d0aaa395 100644\n> > --- a/test/file-descriptor.cpp\n> > +++ b/test/shared-fd.cpp\n> > @@ -2,7 +2,7 @@\n> >  /*\n> >   * Copyright (C) 2019, Google Inc.\n> >   *\n> > - * file_descriptor.cpp - FileDescriptor test\n> > + * shared_fd.cpp - SharedFD test\n> >   */\n> >\n> >  #include <fcntl.h>\n> > @@ -11,7 +11,7 @@\n> >  #include <sys/types.h>\n> >  #include <unistd.h>\n> >\n> > -#include <libcamera/base/file_descriptor.h>\n> > +#include <libcamera/base/shared_fd.h>\n> >  #include <libcamera/base/utils.h>\n> >\n> >  #include \"test.h\"\n> > @@ -19,7 +19,7 @@\n> >  using namespace libcamera;\n> >  using namespace std;\n> >\n> > -class FileDescriptorTest : public Test\n> > +class SharedFDTest : public Test\n> >  {\n> >  protected:\n> >  \tint init()\n> > @@ -43,8 +43,8 @@ protected:\n> >\n> >  \tint run()\n> >  \t{\n> > -\t\t/* Test creating empty FileDescriptor. */\n> > -\t\tdesc1_ = new FileDescriptor();\n> > +\t\t/* Test creating empty SharedFD. */\n> > +\t\tdesc1_ = new SharedFD();\n> >\n> >  \t\tif (desc1_->fd() != -1) {\n> >  \t\t\tstd::cout << \"Failed fd numerical check (default constructor)\"\n> > @@ -56,10 +56,10 @@ protected:\n> >  \t\tdesc1_ = nullptr;\n> >\n> >  \t\t/*\n> > -\t\t * Test creating FileDescriptor by copying numerical file\n> > +\t\t * Test creating SharedFD by copying numerical file\n> >  \t\t * descriptor.\n> >  \t\t */\n> > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > +\t\tdesc1_ = new SharedFD(fd_);\n> >  \t\tif (desc1_->fd() == fd_) {\n> >  \t\t\tstd::cout << \"Failed fd numerical check (lvalue ref constructor)\"\n> >  \t\t\t\t  << std::endl;\n> > @@ -84,13 +84,13 @@ protected:\n> >  \t\t}\n> >\n> >  \t\t/*\n> > -\t\t * Test creating FileDescriptor by taking ownership of\n> > +\t\t * Test creating SharedFD by taking ownership of\n> >  \t\t * numerical file descriptor.\n> >  \t\t */\n> >  \t\tint dupFd = dup(fd_);\n> >  \t\tint dupFdCopy = dupFd;\n> >\n> > -\t\tdesc1_ = new FileDescriptor(std::move(dupFd));\n> > +\t\tdesc1_ = new SharedFD(std::move(dupFd));\n> >  \t\tif (desc1_->fd() != dupFdCopy) {\n> >  \t\t\tstd::cout << \"Failed fd numerical check (rvalue ref constructor)\"\n> >  \t\t\t\t  << std::endl;\n> > @@ -114,9 +114,9 @@ protected:\n> >  \t\t\treturn TestFail;\n> >  \t\t}\n> >\n> > -\t\t/* Test creating FileDescriptor from other FileDescriptor. */\n> > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > -\t\tdesc2_ = new FileDescriptor(*desc1_);\n> > +\t\t/* Test creating SharedFD from other SharedFD. */\n> > +\t\tdesc1_ = new SharedFD(fd_);\n> > +\t\tdesc2_ = new SharedFD(*desc1_);\n> >\n> >  \t\tif (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {\n> >  \t\t\tstd::cout << \"Failed fd numerical check (copy constructor)\"\n> > @@ -142,10 +142,10 @@ protected:\n> >  \t\tdelete desc2_;\n> >  \t\tdesc2_ = nullptr;\n> >\n> > -\t\t/* Test creating FileDescriptor by taking over other FileDescriptor. */\n> > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > +\t\t/* Test creating SharedFD by taking over other SharedFD. */\n> > +\t\tdesc1_ = new SharedFD(fd_);\n> >  \t\tfd = desc1_->fd();\n> > -\t\tdesc2_ = new FileDescriptor(std::move(*desc1_));\n> > +\t\tdesc2_ = new SharedFD(std::move(*desc1_));\n> >\n> >  \t\tif (desc1_->fd() != -1 || desc2_->fd() != fd) {\n> >  \t\t\tstd::cout << \"Failed fd numerical check (move constructor)\"\n> > @@ -164,9 +164,9 @@ protected:\n> >  \t\tdelete desc2_;\n> >  \t\tdesc2_ = nullptr;\n> >\n> > -\t\t/* Test creating FileDescriptor by copy assignment. */\n> > -\t\tdesc1_ = new FileDescriptor();\n> > -\t\tdesc2_ = new FileDescriptor(fd_);\n> > +\t\t/* Test creating SharedFD by copy assignment. */\n> > +\t\tdesc1_ = new SharedFD();\n> > +\t\tdesc2_ = new SharedFD(fd_);\n> >\n> >  \t\tfd = desc2_->fd();\n> >  \t\t*desc1_ = *desc2_;\n> > @@ -188,9 +188,9 @@ protected:\n> >  \t\tdelete desc2_;\n> >  \t\tdesc2_ = nullptr;\n> >\n> > -\t\t/* Test creating FileDescriptor by move assignment. */\n> > -\t\tdesc1_ = new FileDescriptor();\n> > -\t\tdesc2_ = new FileDescriptor(fd_);\n> > +\t\t/* Test creating SharedFD by move assignment. */\n> > +\t\tdesc1_ = new SharedFD();\n> > +\t\tdesc2_ = new SharedFD(fd_);\n> >\n> >  \t\tfd = desc2_->fd();\n> >  \t\t*desc1_ = std::move(*desc2_);\n> > @@ -237,7 +237,7 @@ private:\n> >\n> >  \tint fd_;\n> >  \tino_t inodeNr_;\n> > -\tFileDescriptor *desc1_, *desc2_;\n> > +\tSharedFD *desc1_, *desc2_;\n> >  };\n> >\n> > -TEST_REGISTER(FileDescriptorTest)\n> > +TEST_REGISTER(SharedFDTest)\n> > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > index d856339aa9ee..c37c4941b528 100644\n> > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> >  void {{proxy_name}}::{{method.mojom_name}}IPC(\n> >  \tstd::vector<uint8_t>::const_iterator data,\n> >  \tsize_t dataSize,\n> > -\t[[maybe_unused]] const std::vector<FileDescriptor> &fds)\n> > +\t[[maybe_unused]] const std::vector<SharedFD> &fds)\n> >  {\n> >  {%- for param in method.parameters %}\n> >  \t{{param|name}} {{param.mojom_name}};\n> > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > index ce396c183d0c..c308dd10c7e5 100644\n> > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > @@ -64,7 +64,7 @@ private:\n> >  \tvoid {{method.mojom_name}}IPC(\n> >  \t\tstd::vector<uint8_t>::const_iterator data,\n> >  \t\tsize_t dataSize,\n> > -\t\tconst std::vector<FileDescriptor> &fds);\n> > +\t\tconst std::vector<SharedFD> &fds);\n> >  {% endfor %}\n> >\n> >  \t/* Helper class to invoke async functions in another thread. */\n> > diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > index ebcd2aaaafae..bac826a74c2d 100644\n> > --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > @@ -54,7 +54,7 @@\n> >  {%- for param in params %}\n> >  \tstd::vector<uint8_t> {{param.mojom_name}}Buf;\n> >  {%- if param|has_fd %}\n> > -\tstd::vector<FileDescriptor> {{param.mojom_name}}Fds;\n> > +\tstd::vector<SharedFD> {{param.mojom_name}}Fds;\n> >  \tstd::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =\n> >  {%- else %}\n> >  \tstd::tie({{param.mojom_name}}Buf, std::ignore) =\n> > diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > index b8ef8e7b974e..77bae36fe6b7 100644\n> > --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > @@ -40,7 +40,7 @@\n> >  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> >  {%- elif field|is_fd %}\n> >  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n> > -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> > +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n> >  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n> >  \t\t\tIPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});\n> >  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> > @@ -58,7 +58,7 @@\n> >  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}\n> >  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n> >  \t{%- if field|has_fd %}\n> > -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> > +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n> >  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n> >  \t{%- else %}\n> >  \t\tstd::tie({{field.mojom_name}}, std::ignore) =\n> > @@ -177,7 +177,7 @@\n> >   # \\a struct.\n> >   #}\n> >  {%- macro serializer(struct, namespace) %}\n> > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> >  \tserialize(const {{struct|name_full}} &data,\n> >  {%- if struct|needs_control_serializer %}\n> >  \t\t  ControlSerializer *cs)\n> > @@ -187,7 +187,7 @@\n> >  \t{\n> >  \t\tstd::vector<uint8_t> retData;\n> >  {%- if struct|has_fd %}\n> > -\t\tstd::vector<FileDescriptor> retFds;\n> > +\t\tstd::vector<SharedFD> retFds;\n> >  {%- endif %}\n> >  {%- for field in struct.fields %}\n> >  {{serializer_field(field, namespace, loop)}}\n> > @@ -210,7 +210,7 @@\n> >  {%- macro deserializer_fd(struct, namespace) %}\n> >  \tstatic {{struct|name_full}}\n> >  \tdeserialize(std::vector<uint8_t> &data,\n> > -\t\t    std::vector<FileDescriptor> &fds,\n> > +\t\t    std::vector<SharedFD> &fds,\n> >  {%- if struct|needs_control_serializer %}\n> >  \t\t    ControlSerializer *cs)\n> >  {%- else %}\n> > @@ -224,8 +224,8 @@\n> >  \tstatic {{struct|name_full}}\n> >  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t    std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t    std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t    std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t    std::vector<SharedFD>::const_iterator fdsEnd,\n> >  {%- if struct|needs_control_serializer %}\n> >  \t\t    ControlSerializer *cs)\n> >  {%- else %}\n> > @@ -234,7 +234,7 @@\n> >  \t{\n> >  \t\t{{struct|name_full}} ret;\n> >  \t\tstd::vector<uint8_t>::const_iterator m = dataBegin;\n> > -\t\tstd::vector<FileDescriptor>::const_iterator n = fdsBegin;\n> > +\t\tstd::vector<SharedFD>::const_iterator n = fdsBegin;\n> >\n> >  \t\tsize_t dataSize = std::distance(dataBegin, dataEnd);\n> >  \t\t[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);\n> > @@ -255,7 +255,7 @@\n> >  {%- macro deserializer_fd_simple(struct, namespace) %}\n> >  \tstatic {{struct|name_full}}\n> >  \tdeserialize(std::vector<uint8_t> &data,\n> > -\t\t    [[maybe_unused]] std::vector<FileDescriptor> &fds,\n> > +\t\t    [[maybe_unused]] std::vector<SharedFD> &fds,\n> >  \t\t    ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);\n> > @@ -264,8 +264,8 @@\n> >  \tstatic {{struct|name_full}}\n> >  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> >  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> >  \t\t    ControlSerializer *cs = nullptr)\n> >  \t{\n> >  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);\n> > diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py\n> > index c609f4e5c062..753bfc734e56 100644\n> > --- a/utils/ipc/generators/mojom_libcamera_generator.py\n> > +++ b/utils/ipc/generators/mojom_libcamera_generator.py\n> > @@ -77,7 +77,7 @@ def GetDefaultValue(element):\n> >      if mojom.IsEnumKind(element.kind):\n> >          return f'static_cast<{element.kind.mojom_name}>(0)'\n> >      if isinstance(element.kind, mojom.Struct) and \\\n> > -       element.kind.mojom_name == 'FileDescriptor':\n> > +       element.kind.mojom_name == 'SharedFD':\n> >          return '-1'\n> >      return ''\n> >\n> > @@ -140,7 +140,7 @@ def HasFd(element):\n> >          types = GetAllTypes(element)\n> >      else:\n> >          types = GetAllTypes(element.kind)\n> > -    return \"FileDescriptor\" in types or (attrs is not None and \"hasFd\" in attrs)\n> > +    return \"SharedFD\" in types or (attrs is not None and \"hasFd\" in attrs)\n> >\n> >  def WithDefaultValues(element):\n> >      return [x for x in element if HasDefaultValue(x)]\n> > @@ -221,7 +221,7 @@ def IsEnum(element):\n> >      return mojom.IsEnumKind(element.kind)\n> >\n> >  def IsFd(element):\n> > -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"FileDescriptor\"\n> > +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"SharedFD\"\n> >\n> >  def IsMap(element):\n> >      return mojom.IsMapKind(element.kind)","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 7B3C7BF415\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Nov 2021 17:25:14 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2F1C4605A6;\n\tMon, 29 Nov 2021 18:25:14 +0100 (CET)","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 D681B60592\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 18:25:11 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3613D2A5;\n\tMon, 29 Nov 2021 18:25:11 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"pY5ULETY\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1638206711;\n\tbh=vx59Yei0r7qPBVboL2sICSmK9JcRF4FDJp6BOTxKX4A=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=pY5ULETYeYG+SE9IwfmYjCpEimuobVgp+Wu1gnM9vC4aWJTUkAqdtl2JdPLa5o1Xe\n\tg9vE+EaElyBv/2Rm8OCZxTZXRiHDAGtwOMw7X43akjnyLOlhpMlTOwnMWxv7E/Pg+8\n\ttVs1V+qFz3Ebq1DwlbFcPe2WFd5MnAliJ1sshSi0=","Date":"Mon, 29 Nov 2021 19:24:47 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Message-ID":"<YaUM35YDv12OEerK@pendragon.ideasonboard.com>","References":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>\n\t<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>\n\t<20211129160055.p74ha6iidz7gxxqm@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20211129160055.p74ha6iidz7gxxqm@uno.localdomain>","Subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":21369,"web_url":"https://patchwork.libcamera.org/comment/21369/","msgid":"<20211129173009.g6kggmxvheqokv5f@uno.localdomain>","date":"2021-11-29T17:30:09","subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent\n\nOn Mon, Nov 29, 2021 at 07:24:47PM +0200, Laurent Pinchart wrote:\n> Hi Jacopo,\n>\n> On Mon, Nov 29, 2021 at 05:00:55PM +0100, Jacopo Mondi wrote:\n> > On Mon, Nov 29, 2021 at 01:57:52AM +0200, Laurent Pinchart wrote:\n> > > Now that we have a UniqueFD class, the name FileDescriptor is ambiguous.\n> > > Rename it to SharedFD.\n> > >\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> >\n> > makes sense!\n>\n> Does this mean Rb ? :-)\n>\n\nups :)\nReviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n\nIt does!\n\n> > > ---\n> > >  include/libcamera/base/file.h                 |   4 +-\n> > >  include/libcamera/base/meson.build            |   2 +-\n> > >  .../base/{file_descriptor.h => shared_fd.h}   |  20 +-\n> > >  include/libcamera/framebuffer.h               |   4 +-\n> > >  .../libcamera/internal/ipa_data_serializer.h  |  40 +--\n> > >  include/libcamera/internal/ipc_pipe.h         |   8 +-\n> > >  include/libcamera/ipa/core.mojom              |   6 +-\n> > >  include/libcamera/ipa/raspberrypi.mojom       |   2 +-\n> > >  src/android/camera_device.cpp                 |   2 +-\n> > >  src/ipa/raspberrypi/raspberrypi.cpp           |   4 +-\n> > >  src/libcamera/base/file.cpp                   |   6 +-\n> > >  src/libcamera/base/file_descriptor.cpp        | 266 ------------------\n> > >  src/libcamera/base/meson.build                |   2 +-\n> > >  src/libcamera/base/shared_fd.cpp              | 262 +++++++++++++++++\n> > >  src/libcamera/framebuffer.cpp                 |   6 +-\n> > >  src/libcamera/ipa_data_serializer.cpp         | 100 +++----\n> > >  src/libcamera/ipc_pipe.cpp                    |   4 +-\n> > >  .../pipeline/raspberrypi/raspberrypi.cpp      |   6 +-\n> > >  src/libcamera/v4l2_videodevice.cpp            |   6 +-\n> > >  src/v4l2/v4l2_camera.h                        |   2 +-\n> > >  test/meson.build                              |   2 +-\n> > >  .../ipa_data_serializer_test.cpp              |  14 +-\n> > >  test/{file-descriptor.cpp => shared-fd.cpp}   |  46 +--\n> > >  .../module_ipa_proxy.cpp.tmpl                 |   2 +-\n> > >  .../module_ipa_proxy.h.tmpl                   |   2 +-\n> > >  .../libcamera_templates/proxy_functions.tmpl  |   2 +-\n> > >  .../libcamera_templates/serializer.tmpl       |  22 +-\n> > >  .../generators/mojom_libcamera_generator.py   |   6 +-\n> > >  28 files changed, 422 insertions(+), 426 deletions(-)\n> > >  rename include/libcamera/base/{file_descriptor.h => shared_fd.h} (55%)\n> > >  delete mode 100644 src/libcamera/base/file_descriptor.cpp\n> > >  create mode 100644 src/libcamera/base/shared_fd.cpp\n> > >  rename test/{file-descriptor.cpp => shared-fd.cpp} (80%)\n> > >\n> > > diff --git a/include/libcamera/base/file.h b/include/libcamera/base/file.h\n> > > index 47769da7abc2..691b52d6ab2d 100644\n> > > --- a/include/libcamera/base/file.h\n> > > +++ b/include/libcamera/base/file.h\n> > > @@ -21,7 +21,7 @@\n> > >\n> > >  namespace libcamera {\n> > >\n> > > -class FileDescriptor;\n> > > +class SharedFD;\n> > >\n> > >  class File\n> > >  {\n> > > @@ -69,7 +69,7 @@ public:\n> > >  \tbool unmap(uint8_t *addr);\n> > >\n> > >  \tstatic bool exists(const std::string &name);\n> > > -\tstatic ino_t inode(const FileDescriptor &fd);\n> > > +\tstatic ino_t inode(const SharedFD &fd);\n> > >\n> > >  private:\n> > >  \tLIBCAMERA_DISABLE_COPY(File)\n> > > diff --git a/include/libcamera/base/meson.build b/include/libcamera/base/meson.build\n> > > index cca374a769cc..112420dab225 100644\n> > > --- a/include/libcamera/base/meson.build\n> > > +++ b/include/libcamera/base/meson.build\n> > > @@ -11,13 +11,13 @@ libcamera_base_headers = files([\n> > >      'event_dispatcher_poll.h',\n> > >      'event_notifier.h',\n> > >      'file.h',\n> > > -    'file_descriptor.h',\n> > >      'flags.h',\n> > >      'log.h',\n> > >      'message.h',\n> > >      'object.h',\n> > >      'private.h',\n> > >      'semaphore.h',\n> > > +    'shared_fd.h',\n> > >      'signal.h',\n> > >      'span.h',\n> > >      'thread.h',\n> > > diff --git a/include/libcamera/base/file_descriptor.h b/include/libcamera/base/shared_fd.h\n> > > similarity index 55%\n> > > rename from include/libcamera/base/file_descriptor.h\n> > > rename to include/libcamera/base/shared_fd.h\n> > > index 12a43f95d414..a786885ceb32 100644\n> > > --- a/include/libcamera/base/file_descriptor.h\n> > > +++ b/include/libcamera/base/shared_fd.h\n> > > @@ -2,7 +2,7 @@\n> > >  /*\n> > >   * Copyright (C) 2019, Google Inc.\n> > >   *\n> > > - * file_descriptor.h - File descriptor wrapper\n> > > + * shared_fd.h - File descriptor wrapper with shared ownership\n> > >   */\n> > >\n> > >  #pragma once\n> > > @@ -13,18 +13,18 @@ namespace libcamera {\n> > >\n> > >  class UniqueFD;\n> > >\n> > > -class FileDescriptor final\n> > > +class SharedFD final\n> > >  {\n> > >  public:\n> > > -\texplicit FileDescriptor(const int &fd = -1);\n> > > -\texplicit FileDescriptor(int &&fd);\n> > > -\texplicit FileDescriptor(UniqueFD fd);\n> > > -\tFileDescriptor(const FileDescriptor &other);\n> > > -\tFileDescriptor(FileDescriptor &&other);\n> > > -\t~FileDescriptor();\n> > > +\texplicit SharedFD(const int &fd = -1);\n> > > +\texplicit SharedFD(int &&fd);\n> > > +\texplicit SharedFD(UniqueFD fd);\n> > > +\tSharedFD(const SharedFD &other);\n> > > +\tSharedFD(SharedFD &&other);\n> > > +\t~SharedFD();\n> > >\n> > > -\tFileDescriptor &operator=(const FileDescriptor &other);\n> > > -\tFileDescriptor &operator=(FileDescriptor &&other);\n> > > +\tSharedFD &operator=(const SharedFD &other);\n> > > +\tSharedFD &operator=(SharedFD &&other);\n> > >\n> > >  \tbool isValid() const { return fd_ != nullptr; }\n> > >  \tint fd() const { return fd_ ? fd_->fd() : -1; }\n> > > diff --git a/include/libcamera/framebuffer.h b/include/libcamera/framebuffer.h\n> > > index 2fbea9c5be16..357bbe189551 100644\n> > > --- a/include/libcamera/framebuffer.h\n> > > +++ b/include/libcamera/framebuffer.h\n> > > @@ -13,7 +13,7 @@\n> > >  #include <vector>\n> > >\n> > >  #include <libcamera/base/class.h>\n> > > -#include <libcamera/base/file_descriptor.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >  #include <libcamera/base/span.h>\n> > >\n> > >  namespace libcamera {\n> > > @@ -51,7 +51,7 @@ class FrameBuffer final : public Extensible\n> > >  public:\n> > >  \tstruct Plane {\n> > >  \t\tstatic constexpr unsigned int kInvalidOffset = std::numeric_limits<unsigned int>::max();\n> > > -\t\tFileDescriptor fd;\n> > > +\t\tSharedFD fd;\n> > >  \t\tunsigned int offset = kInvalidOffset;\n> > >  \t\tunsigned int length;\n> > >  \t};\n> > > diff --git a/include/libcamera/internal/ipa_data_serializer.h b/include/libcamera/internal/ipa_data_serializer.h\n> > > index c2f602d5b7de..a87449c9be48 100644\n> > > --- a/include/libcamera/internal/ipa_data_serializer.h\n> > > +++ b/include/libcamera/internal/ipa_data_serializer.h\n> > > @@ -66,7 +66,7 @@ template<typename T>\n> > >  class IPADataSerializer\n> > >  {\n> > >  public:\n> > > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  \tserialize(const T &data, ControlSerializer *cs = nullptr);\n> > >\n> > >  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> > > @@ -76,12 +76,12 @@ public:\n> > >  \t\t\t     ControlSerializer *cs = nullptr);\n> > >\n> > >  \tstatic T deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t     const std::vector<FileDescriptor> &fds,\n> > > +\t\t\t     const std::vector<SharedFD> &fds,\n> > >  \t\t\t     ControlSerializer *cs = nullptr);\n> > >  \tstatic T deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t     std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t     std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t     std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t     std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t     ControlSerializer *cs = nullptr);\n> > >  };\n> > >\n> > > @@ -104,11 +104,11 @@ template<typename V>\n> > >  class IPADataSerializer<std::vector<V>>\n> > >  {\n> > >  public:\n> > > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  \tserialize(const std::vector<V> &data, ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\tstd::vector<uint8_t> dataVec;\n> > > -\t\tstd::vector<FileDescriptor> fdsVec;\n> > > +\t\tstd::vector<SharedFD> fdsVec;\n> > >\n> > >  \t\t/* Serialize the length. */\n> > >  \t\tuint32_t vecLen = data.size();\n> > > @@ -117,7 +117,7 @@ public:\n> > >  \t\t/* Serialize the members. */\n> > >  \t\tfor (auto const &it : data) {\n> > >  \t\t\tstd::vector<uint8_t> dvec;\n> > > -\t\t\tstd::vector<FileDescriptor> fvec;\n> > > +\t\t\tstd::vector<SharedFD> fvec;\n> > >\n> > >  \t\t\tstd::tie(dvec, fvec) =\n> > >  \t\t\t\tIPADataSerializer<V>::serialize(it, cs);\n> > > @@ -141,11 +141,11 @@ public:\n> > >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > > -\t\tstd::vector<FileDescriptor> fds;\n> > > +\t\tstd::vector<SharedFD> fds;\n> > >  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> > >  \t}\n> > >\n> > > -\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> > > +\tstatic std::vector<V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > > @@ -153,15 +153,15 @@ public:\n> > >\n> > >  \tstatic std::vector<V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\tuint32_t vecLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > >  \t\tstd::vector<V> ret(vecLen);\n> > >\n> > >  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> > > -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> > > +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n> > >  \t\tfor (uint32_t i = 0; i < vecLen; i++) {\n> > >  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > >  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > > @@ -201,11 +201,11 @@ template<typename K, typename V>\n> > >  class IPADataSerializer<std::map<K, V>>\n> > >  {\n> > >  public:\n> > > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  \tserialize(const std::map<K, V> &data, ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\tstd::vector<uint8_t> dataVec;\n> > > -\t\tstd::vector<FileDescriptor> fdsVec;\n> > > +\t\tstd::vector<SharedFD> fdsVec;\n> > >\n> > >  \t\t/* Serialize the length. */\n> > >  \t\tuint32_t mapLen = data.size();\n> > > @@ -214,7 +214,7 @@ public:\n> > >  \t\t/* Serialize the members. */\n> > >  \t\tfor (auto const &it : data) {\n> > >  \t\t\tstd::vector<uint8_t> dvec;\n> > > -\t\t\tstd::vector<FileDescriptor> fvec;\n> > > +\t\t\tstd::vector<SharedFD> fvec;\n> > >\n> > >  \t\t\tstd::tie(dvec, fvec) =\n> > >  \t\t\t\tIPADataSerializer<K>::serialize(it.first, cs);\n> > > @@ -247,11 +247,11 @@ public:\n> > >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > > -\t\tstd::vector<FileDescriptor> fds;\n> > > +\t\tstd::vector<SharedFD> fds;\n> > >  \t\treturn deserialize(dataBegin, dataEnd, fds.cbegin(), fds.end(), cs);\n> > >  \t}\n> > >\n> > > -\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<FileDescriptor> &fds,\n> > > +\tstatic std::map<K, V> deserialize(std::vector<uint8_t> &data, std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > > @@ -259,8 +259,8 @@ public:\n> > >\n> > >  \tstatic std::map<K, V> deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t  std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t  ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\tstd::map<K, V> ret;\n> > > @@ -268,7 +268,7 @@ public:\n> > >  \t\tuint32_t mapLen = readPOD<uint32_t>(dataBegin, 0, dataEnd);\n> > >\n> > >  \t\tstd::vector<uint8_t>::const_iterator dataIter = dataBegin + 4;\n> > > -\t\tstd::vector<FileDescriptor>::const_iterator fdIter = fdsBegin;\n> > > +\t\tstd::vector<SharedFD>::const_iterator fdIter = fdsBegin;\n> > >  \t\tfor (uint32_t i = 0; i < mapLen; i++) {\n> > >  \t\t\tuint32_t sizeofData = readPOD<uint32_t>(dataIter, 0, dataEnd);\n> > >  \t\t\tuint32_t sizeofFds  = readPOD<uint32_t>(dataIter, 4, dataEnd);\n> > > diff --git a/include/libcamera/internal/ipc_pipe.h b/include/libcamera/internal/ipc_pipe.h\n> > > index 986f8d886fa6..ab5dd67c3813 100644\n> > > --- a/include/libcamera/internal/ipc_pipe.h\n> > > +++ b/include/libcamera/internal/ipc_pipe.h\n> > > @@ -9,7 +9,7 @@\n> > >\n> > >  #include <vector>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >  #include <libcamera/base/signal.h>\n> > >\n> > >  #include \"libcamera/internal/ipc_unixsocket.h\"\n> > > @@ -33,17 +33,17 @@ public:\n> > >\n> > >  \tHeader &header() { return header_; }\n> > >  \tstd::vector<uint8_t> &data() { return data_; }\n> > > -\tstd::vector<FileDescriptor> &fds() { return fds_; }\n> > > +\tstd::vector<SharedFD> &fds() { return fds_; }\n> > >\n> > >  \tconst Header &header() const { return header_; }\n> > >  \tconst std::vector<uint8_t> &data() const { return data_; }\n> > > -\tconst std::vector<FileDescriptor> &fds() const { return fds_; }\n> > > +\tconst std::vector<SharedFD> &fds() const { return fds_; }\n> > >\n> > >  private:\n> > >  \tHeader header_;\n> > >\n> > >  \tstd::vector<uint8_t> data_;\n> > > -\tstd::vector<FileDescriptor> fds_;\n> > > +\tstd::vector<SharedFD> fds_;\n> > >  };\n> > >\n> > >  class IPCPipe\n> > > diff --git a/include/libcamera/ipa/core.mojom b/include/libcamera/ipa/core.mojom\n> > > index f7eff0c7ab8c..74f3339e56f2 100644\n> > > --- a/include/libcamera/ipa/core.mojom\n> > > +++ b/include/libcamera/ipa/core.mojom\n> > > @@ -32,7 +32,7 @@ module libcamera;\n> > >   *   - This attribute instructs the build system that a (de)serializer is\n> > >   *     available for the type and there's no need to generate one\n> > >   * - hasFd - struct fields or empty structs only\n> > > - *   - Designate that this field or empty struct contains a FileDescriptor\n> > > + *   - Designate that this field or empty struct contains a SharedFD\n> > >   *\n> > >   * Rules:\n> > >   * - If the type is defined in a libcamera C++ header *and* a (de)serializer is\n> > > @@ -60,7 +60,7 @@ module libcamera;\n> > >   *     - In mojom, reference the type as FrameBuffer.Plane and only as map/array\n> > >   *       member\n> > >   * - [skipHeader] and [skipSerdes] only work here in core.mojom\n> > > - * - If a field in a struct has a FileDescriptor, but is not explicitly\n> > > + * - If a field in a struct has a SharedFD, but is not explicitly\n> > >   *   defined so in mojom, then the field must be marked with the [hasFd]\n> > >   *   attribute\n> > >   *\n> > > @@ -71,7 +71,7 @@ module libcamera;\n> > >   */\n> > >  [skipSerdes, skipHeader] struct ControlInfoMap {};\n> > >  [skipSerdes, skipHeader] struct ControlList {};\n> > > -[skipSerdes, skipHeader] struct FileDescriptor {};\n> > > +[skipSerdes, skipHeader] struct SharedFD {};\n> > >\n> > >  [skipHeader] struct Point {\n> > >  \tint32 x;\n> > > diff --git a/include/libcamera/ipa/raspberrypi.mojom b/include/libcamera/ipa/raspberrypi.mojom\n> > > index e453d46cb14f..acd3cafe6c91 100644\n> > > --- a/include/libcamera/ipa/raspberrypi.mojom\n> > > +++ b/include/libcamera/ipa/raspberrypi.mojom\n> > > @@ -35,7 +35,7 @@ struct ISPConfig {\n> > >\n> > >  struct IPAConfig {\n> > >  \tuint32 transform;\n> > > -\tlibcamera.FileDescriptor lsTableHandle;\n> > > +\tlibcamera.SharedFD lsTableHandle;\n> > >  };\n> > >\n> > >  struct StartConfig {\n> > > diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp\n> > > index f2e0bdbdbbf6..1938b10509fa 100644\n> > > --- a/src/android/camera_device.cpp\n> > > +++ b/src/android/camera_device.cpp\n> > > @@ -725,7 +725,7 @@ CameraDevice::createFrameBuffer(const buffer_handle_t camera3buffer,\n> > >\n> > >  \tstd::vector<FrameBuffer::Plane> planes(buf.numPlanes());\n> > >  \tfor (size_t i = 0; i < buf.numPlanes(); ++i) {\n> > > -\t\tFileDescriptor fd{ camera3buffer->data[i] };\n> > > +\t\tSharedFD fd{ camera3buffer->data[i] };\n> > >  \t\tif (!fd.isValid()) {\n> > >  \t\t\tLOG(HAL, Fatal) << \"No valid fd\";\n> > >  \t\t\treturn nullptr;\n> > > diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp\n> > > index c6aec09046f7..aaf629eeb3fc 100644\n> > > --- a/src/ipa/raspberrypi/raspberrypi.cpp\n> > > +++ b/src/ipa/raspberrypi/raspberrypi.cpp\n> > > @@ -15,8 +15,8 @@\n> > >\n> > >  #include <linux/bcm2835-isp.h>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > >  #include <libcamera/base/log.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >  #include <libcamera/base/span.h>\n> > >\n> > >  #include <libcamera/control_ids.h>\n> > > @@ -164,7 +164,7 @@ private:\n> > >  \tbool processPending_;\n> > >\n> > >  \t/* LS table allocation passed in from the pipeline handler. */\n> > > -\tFileDescriptor lsTableHandle_;\n> > > +\tSharedFD lsTableHandle_;\n> > >  \tvoid *lsTable_;\n> > >\n> > >  \t/* Distinguish the first camera start from others. */\n> > > diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp\n> > > index 66c73c406198..3ca9839bb989 100644\n> > > --- a/src/libcamera/base/file.cpp\n> > > +++ b/src/libcamera/base/file.cpp\n> > > @@ -14,8 +14,8 @@\n> > >  #include <sys/types.h>\n> > >  #include <unistd.h>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > >  #include <libcamera/base/log.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >\n> > >  /**\n> > >   * \\file base/file.h\n> > > @@ -473,11 +473,11 @@ bool File::exists(const std::string &name)\n> > >  }\n> > >\n> > >  /**\n> > > - * \\brief Retrieve the inode of a FileDescriptor\n> > > + * \\brief Retrieve the inode of a SharedFD\n> > >   *\n> > >   * \\return The file descriptor inode on success, or 0 on error\n> > >   */\n> > > -ino_t File::inode(const FileDescriptor &fd)\n> > > +ino_t File::inode(const SharedFD &fd)\n> > >  {\n> > >  \tif (!fd.isValid())\n> > >  \t\treturn 0;\n> > > diff --git a/src/libcamera/base/file_descriptor.cpp b/src/libcamera/base/file_descriptor.cpp\n> > > deleted file mode 100644\n> > > index a83bf52c31e6..000000000000\n> > > --- a/src/libcamera/base/file_descriptor.cpp\n> > > +++ /dev/null\n> > > @@ -1,266 +0,0 @@\n> > > -/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > -/*\n> > > - * Copyright (C) 2019, Google Inc.\n> > > - *\n> > > - * file_descriptor.cpp - File descriptor wrapper\n> > > - */\n> > > -\n> > > -#include <libcamera/base/file_descriptor.h>\n> > > -\n> > > -#include <string.h>\n> > > -#include <sys/types.h>\n> > > -#include <unistd.h>\n> > > -#include <utility>\n> > > -\n> > > -#include <libcamera/base/log.h>\n> > > -#include <libcamera/base/unique_fd.h>\n> > > -\n> > > -/**\n> > > - * \\file base/file_descriptor.h\n> > > - * \\brief File descriptor wrapper\n> > > - */\n> > > -\n> > > -namespace libcamera {\n> > > -\n> > > -LOG_DEFINE_CATEGORY(FileDescriptor)\n> > > -\n> > > -/**\n> > > - * \\class FileDescriptor\n> > > - * \\brief RAII-style wrapper for file descriptors\n> > > - *\n> > > - * The FileDescriptor class provides RAII-style lifetime management of file\n> > > - * descriptors with an efficient mechanism for ownership sharing. At its core,\n> > > - * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> > > - * integer) with an RAII-style interface. The Descriptor is then implicitly\n> > > - * shared with all FileDescriptor instances constructed as copies.\n> > > - *\n> > > - * When constructed from a numerical file descriptor, the FileDescriptor\n> > > - * instance either duplicates or takes over the file descriptor:\n> > > - *\n> > > - * - The FileDescriptor(const int &) constructor duplicates the numerical file\n> > > - *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> > > - *   responsible for closing the original file descriptor, and the value\n> > > - *   returned by fd() will be different from the value passed to the\n> > > - *   constructor.\n> > > - *\n> > > - * - The FileDescriptor(int &&) constructor takes over the numerical file\n> > > - *   descriptor and wraps it in a Descriptor. The caller shall not touch the\n> > > - *   original file descriptor once the function returns, and the value returned\n> > > - *   by fd() will be identical to the value passed to the constructor.\n> > > - *\n> > > - * The copy constructor and assignment operator create copies that share the\n> > > - * Descriptor, while the move versions of those functions additionally make the\n> > > - * other FileDescriptor invalid. When the last FileDescriptor that references a\n> > > - * Descriptor is destroyed, the file descriptor is closed.\n> > > - *\n> > > - * The numerical file descriptor is available through the fd() function. All\n> > > - * FileDescriptor instances created as copies of a FileDescriptor will report\n> > > - * the same fd() value. Callers can perform operations on the fd(), but shall\n> > > - * never close it manually.\n> > > - */\n> > > -\n> > > -/**\n> > > - * \\brief Create a FileDescriptor copying a given \\a fd\n> > > - * \\param[in] fd File descriptor\n> > > - *\n> > > - * Construct a FileDescriptor from a numerical file descriptor by duplicating\n> > > - * the \\a fd, and take ownership of the copy. The original \\a fd is left\n> > > - * untouched, and the caller is responsible for closing it when appropriate.\n> > > - * The duplicated file descriptor will be closed automatically when all\n> > > - * FileDescriptor instances that reference it are destroyed.\n> > > - *\n> > > - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> > > - * the fd() function will return -1.\n> > > - */\n> > > -FileDescriptor::FileDescriptor(const int &fd)\n> > > -{\n> > > -\tif (fd < 0)\n> > > -\t\treturn;\n> > > -\n> > > -\tfd_ = std::make_shared<Descriptor>(fd, true);\n> > > -\tif (fd_->fd() < 0)\n> > > -\t\tfd_.reset();\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Create a FileDescriptor taking ownership of a given \\a fd\n> > > - * \\param[in] fd File descriptor\n> > > - *\n> > > - * Construct a FileDescriptor from a numerical file descriptor by taking\n> > > - * ownership of the \\a fd. The original \\a fd is set to -1 and shall not be\n> > > - * touched by the caller anymore. In particular, the caller shall not close the\n> > > - * original \\a fd manually. The duplicated file descriptor will be closed\n> > > - * automatically when all FileDescriptor instances that reference it are\n> > > - * destroyed.\n> > > - *\n> > > - * If the \\a fd is negative, the FileDescriptor is constructed as invalid and\n> > > - * the fd() function will return -1.\n> > > - */\n> > > -FileDescriptor::FileDescriptor(int &&fd)\n> > > -{\n> > > -\tif (fd < 0)\n> > > -\t\treturn;\n> > > -\n> > > -\tfd_ = std::make_shared<Descriptor>(fd, false);\n> > > -\t/*\n> > > -\t * The Descriptor constructor can't have failed here, as it took over\n> > > -\t * the fd without duplicating it. Just set the original fd to -1 to\n> > > -\t * implement move semantics.\n> > > -\t */\n> > > -\tfd = -1;\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Create a FileDescriptor taking ownership of a given UniqueFD \\a fd\n> > > - * \\param[in] fd UniqueFD\n> > > - *\n> > > - * Construct a FileDescriptor from UniqueFD by taking ownership of the \\a fd.\n> > > - * The original \\a fd becomes invalid.\n> > > - */\n> > > -FileDescriptor::FileDescriptor(UniqueFD fd)\n> > > -\t: FileDescriptor(fd.release())\n> > > -{\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Copy constructor, create a FileDescriptor from a copy of \\a other\n> > > - * \\param[in] other The other FileDescriptor\n> > > - *\n> > > - * Copying a FileDescriptor implicitly shares ownership of the wrapped file\n> > > - * descriptor. The original FileDescriptor is left untouched, and the caller is\n> > > - * responsible for destroying it when appropriate. The wrapped file descriptor\n> > > - * will be closed automatically when all FileDescriptor instances that\n> > > - * reference it are destroyed.\n> > > - */\n> > > -FileDescriptor::FileDescriptor(const FileDescriptor &other)\n> > > -\t: fd_(other.fd_)\n> > > -{\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Move constructor, create a FileDescriptor by taking over \\a other\n> > > - * \\param[in] other The other FileDescriptor\n> > > - *\n> > > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> > > - * by \\a other to the new FileDescriptor. The \\a other FileDescriptor is\n> > > - * invalidated and its fd() function will return -1. The wrapped file descriptor\n> > > - * will be closed automatically when all FileDescriptor instances that\n> > > - * reference it are destroyed.\n> > > - */\n> > > -FileDescriptor::FileDescriptor(FileDescriptor &&other)\n> > > -\t: fd_(std::move(other.fd_))\n> > > -{\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Destroy the FileDescriptor instance\n> > > - *\n> > > - * Destroying a FileDescriptor instance releases its reference to the wrapped\n> > > - * descriptor, if any. When the last instance that references a wrapped\n> > > - * descriptor is destroyed, the file descriptor is automatically closed.\n> > > - */\n> > > -FileDescriptor::~FileDescriptor()\n> > > -{\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> > > - * copy of \\a other\n> > > - * \\param[in] other The other FileDescriptor\n> > > - *\n> > > - * Copying a FileDescriptor creates a new reference to the wrapped file\n> > > - * descriptor owner by \\a other. If \\a other is invalid, *this will also be\n> > > - * invalid. The original FileDescriptor is left untouched, and the caller is\n> > > - * responsible for destroying it when appropriate. The wrapped file descriptor\n> > > - * will be closed automatically when all FileDescriptor instances that\n> > > - * reference it are destroyed.\n> > > - *\n> > > - * \\return A reference to this FileDescriptor\n> > > - */\n> > > -FileDescriptor &FileDescriptor::operator=(const FileDescriptor &other)\n> > > -{\n> > > -\tfd_ = other.fd_;\n> > > -\n> > > -\treturn *this;\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\brief Move assignment operator, replace the wrapped file descriptor by\n> > > - * taking over \\a other\n> > > - * \\param[in] other The other FileDescriptor\n> > > - *\n> > > - * Moving a FileDescriptor moves the reference to the wrapped descriptor owned\n> > > - * by \\a other to the new FileDescriptor. If \\a other is invalid, *this will\n> > > - * also be invalid. The \\a other FileDescriptor is invalidated and its fd()\n> > > - * function will return -1. The wrapped file descriptor will be closed\n> > > - * automatically when all FileDescriptor instances that reference it are\n> > > - * destroyed.\n> > > - *\n> > > - * \\return A reference to this FileDescriptor\n> > > - */\n> > > -FileDescriptor &FileDescriptor::operator=(FileDescriptor &&other)\n> > > -{\n> > > -\tfd_ = std::move(other.fd_);\n> > > -\n> > > -\treturn *this;\n> > > -}\n> > > -\n> > > -/**\n> > > - * \\fn FileDescriptor::isValid()\n> > > - * \\brief Check if the FileDescriptor instance is valid\n> > > - * \\return True if the FileDescriptor is valid, false otherwise\n> > > - */\n> > > -\n> > > -/**\n> > > - * \\fn FileDescriptor::fd()\n> > > - * \\brief Retrieve the numerical file descriptor\n> > > - * \\return The numerical file descriptor, which may be -1 if the FileDescriptor\n> > > - * instance is invalid\n> > > - */\n> > > -\n> > > -/**\n> > > - * \\brief Duplicate a FileDescriptor\n> > > - *\n> > > - * Duplicating a FileDescriptor creates a duplicate of the wrapped file\n> > > - * descriptor and returns a UniqueFD that owns the duplicate. The fd() function\n> > > - * of the original and the get() function of the duplicate will return different\n> > > - * values. The duplicate instance will not be affected by destruction of the\n> > > - * original instance or its copies.\n> > > - *\n> > > - * \\return A UniqueFD owning a duplicate of the original file descriptor\n> > > - */\n> > > -UniqueFD FileDescriptor::dup() const\n> > > -{\n> > > -\tint dupFd = ::dup(fd());\n> > > -\tif (dupFd == -1) {\n> > > -\t\tint ret = -errno;\n> > > -\t\tLOG(FileDescriptor, Error)\n> > > -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > > -\t}\n> > > -\n> > > -\treturn UniqueFD(dupFd);\n> > > -}\n> > > -\n> > > -FileDescriptor::Descriptor::Descriptor(int fd, bool duplicate)\n> > > -{\n> > > -\tif (!duplicate) {\n> > > -\t\tfd_ = fd;\n> > > -\t\treturn;\n> > > -\t}\n> > > -\n> > > -\t/* Failing to dup() a fd should not happen and is fatal. */\n> > > -\tfd_ = ::dup(fd);\n> > > -\tif (fd_ == -1) {\n> > > -\t\tint ret = -errno;\n> > > -\t\tLOG(FileDescriptor, Fatal)\n> > > -\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > > -\t}\n> > > -}\n> > > -\n> > > -FileDescriptor::Descriptor::~Descriptor()\n> > > -{\n> > > -\tif (fd_ != -1)\n> > > -\t\tclose(fd_);\n> > > -}\n> > > -\n> > > -} /* namespace libcamera */\n> > > diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build\n> > > index b0d85bc19245..ccb746c27466 100644\n> > > --- a/src/libcamera/base/meson.build\n> > > +++ b/src/libcamera/base/meson.build\n> > > @@ -8,12 +8,12 @@ libcamera_base_sources = files([\n> > >      'event_dispatcher_poll.cpp',\n> > >      'event_notifier.cpp',\n> > >      'file.cpp',\n> > > -    'file_descriptor.cpp',\n> > >      'flags.cpp',\n> > >      'log.cpp',\n> > >      'message.cpp',\n> > >      'object.cpp',\n> > >      'semaphore.cpp',\n> > > +    'shared_fd.cpp',\n> > >      'signal.cpp',\n> > >      'thread.cpp',\n> > >      'timer.cpp',\n> > > diff --git a/src/libcamera/base/shared_fd.cpp b/src/libcamera/base/shared_fd.cpp\n> > > new file mode 100644\n> > > index 000000000000..05b6892f7e19\n> > > --- /dev/null\n> > > +++ b/src/libcamera/base/shared_fd.cpp\n> > > @@ -0,0 +1,262 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2019, Google Inc.\n> > > + *\n> > > + * shared_fd.cpp - File descriptor wrapper with shared ownership\n> > > + */\n> > > +\n> > > +#include <libcamera/base/shared_fd.h>\n> > > +\n> > > +#include <string.h>\n> > > +#include <sys/types.h>\n> > > +#include <unistd.h>\n> > > +#include <utility>\n> > > +\n> > > +#include <libcamera/base/log.h>\n> > > +#include <libcamera/base/unique_fd.h>\n> > > +\n> > > +/**\n> > > + * \\file base/shared_fd.h\n> > > + * \\brief File descriptor wrapper\n> > > + */\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DEFINE_CATEGORY(SharedFD)\n> > > +\n> > > +/**\n> > > + * \\class SharedFD\n> > > + * \\brief RAII-style wrapper for file descriptors\n> > > + *\n> > > + * The SharedFD class provides RAII-style lifetime management of file\n> > > + * descriptors with an efficient mechanism for ownership sharing. At its core,\n> > > + * an internal Descriptor object wraps a file descriptor (expressed as a signed\n> > > + * integer) with an RAII-style interface. The Descriptor is then implicitly\n> > > + * shared with all SharedFD instances constructed as copies.\n> > > + *\n> > > + * When constructed from a numerical file descriptor, the SharedFD instance\n> > > + * either duplicates or takes over the file descriptor:\n> > > + *\n> > > + * - The SharedFD(const int &) constructor duplicates the numerical file\n> > > + *   descriptor and wraps the duplicate in a Descriptor. The caller is\n> > > + *   responsible for closing the original file descriptor, and the value\n> > > + *   returned by fd() will be different from the value passed to the\n> > > + *   constructor.\n> > > + *\n> > > + * - The SharedFD(int &&) constructor takes over the numerical file descriptor\n> > > + *   and wraps it in a Descriptor. The caller shall not touch the original file\n> > > + *   descriptor once the function returns, and the value returned by fd() will\n> > > + *   be identical to the value passed to the constructor.\n> > > + *\n> > > + * The copy constructor and assignment operator create copies that share the\n> > > + * Descriptor, while the move versions of those functions additionally make the\n> > > + * other SharedFD invalid. When the last SharedFD that references a Descriptor\n> > > + * is destroyed, the file descriptor is closed.\n> > > + *\n> > > + * The numerical file descriptor is available through the fd() function. All\n> > > + * SharedFD instances created as copies of a SharedFD will report the same fd()\n> > > + * value. Callers can perform operations on the fd(), but shall never close it\n> > > + * manually.\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Create a SharedFD copying a given \\a fd\n> > > + * \\param[in] fd File descriptor\n> > > + *\n> > > + * Construct a SharedFD from a numerical file descriptor by duplicating the\n> > > + * \\a fd, and take ownership of the copy. The original \\a fd is left untouched,\n> > > + * and the caller is responsible for closing it when appropriate. The duplicated\n> > > + * file descriptor will be closed automatically when all SharedFD instances that\n> > > + * reference it are destroyed.\n> > > + *\n> > > + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> > > + * function will return -1.\n> > > + */\n> > > +SharedFD::SharedFD(const int &fd)\n> > > +{\n> > > +\tif (fd < 0)\n> > > +\t\treturn;\n> > > +\n> > > +\tfd_ = std::make_shared<Descriptor>(fd, true);\n> > > +\tif (fd_->fd() < 0)\n> > > +\t\tfd_.reset();\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Create a SharedFD taking ownership of a given \\a fd\n> > > + * \\param[in] fd File descriptor\n> > > + *\n> > > + * Construct a SharedFD from a numerical file descriptor by taking ownership of\n> > > + * the \\a fd. The original \\a fd is set to -1 and shall not be touched by the\n> > > + * caller anymore. In particular, the caller shall not close the original \\a fd\n> > > + * manually. The duplicated file descriptor will be closed automatically when\n> > > + * all SharedFD instances that reference it are destroyed.\n> > > + *\n> > > + * If the \\a fd is negative, the SharedFD is constructed as invalid and the fd()\n> > > + * function will return -1.\n> > > + */\n> > > +SharedFD::SharedFD(int &&fd)\n> > > +{\n> > > +\tif (fd < 0)\n> > > +\t\treturn;\n> > > +\n> > > +\tfd_ = std::make_shared<Descriptor>(fd, false);\n> > > +\t/*\n> > > +\t * The Descriptor constructor can't have failed here, as it took over\n> > > +\t * the fd without duplicating it. Just set the original fd to -1 to\n> > > +\t * implement move semantics.\n> > > +\t */\n> > > +\tfd = -1;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Create a SharedFD taking ownership of a given UniqueFD \\a fd\n> > > + * \\param[in] fd UniqueFD\n> > > + *\n> > > + * Construct a SharedFD from UniqueFD by taking ownership of the \\a fd. The\n> > > + * original \\a fd becomes invalid.\n> > > + */\n> > > +SharedFD::SharedFD(UniqueFD fd)\n> > > +\t: SharedFD(fd.release())\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Copy constructor, create a SharedFD from a copy of \\a other\n> > > + * \\param[in] other The other SharedFD\n> > > + *\n> > > + * Copying a SharedFD implicitly shares ownership of the wrapped file\n> > > + * descriptor. The original SharedFD is left untouched, and the caller is\n> > > + * responsible for destroying it when appropriate. The wrapped file descriptor\n> > > + * will be closed automatically when all SharedFD instances that reference it\n> > > + * are destroyed.\n> > > + */\n> > > +SharedFD::SharedFD(const SharedFD &other)\n> > > +\t: fd_(other.fd_)\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Move constructor, create a SharedFD by taking over \\a other\n> > > + * \\param[in] other The other SharedFD\n> > > + *\n> > > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> > > + * \\a other to the new SharedFD. The \\a other SharedFD is invalidated and its\n> > > + * fd() function will return -1. The wrapped file descriptor will be closed\n> > > + * automatically when all SharedFD instances that reference it are destroyed.\n> > > + */\n> > > +SharedFD::SharedFD(SharedFD &&other)\n> > > +\t: fd_(std::move(other.fd_))\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Destroy the SharedFD instance\n> > > + *\n> > > + * Destroying a SharedFD instance releases its reference to the wrapped\n> > > + * descriptor, if any. When the last instance that references a wrapped\n> > > + * descriptor is destroyed, the file descriptor is automatically closed.\n> > > + */\n> > > +SharedFD::~SharedFD()\n> > > +{\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Copy assignment operator, replace the wrapped file descriptor with a\n> > > + * copy of \\a other\n> > > + * \\param[in] other The other SharedFD\n> > > + *\n> > > + * Copying a SharedFD creates a new reference to the wrapped file descriptor\n> > > + * owner by \\a other. If \\a other is invalid, *this will also be invalid. The\n> > > + * original SharedFD is left untouched, and the caller is responsible for\n> > > + * destroying it when appropriate. The wrapped file descriptor will be closed\n> > > + * automatically when all SharedFD instances that reference it are destroyed.\n> > > + *\n> > > + * \\return A reference to this SharedFD\n> > > + */\n> > > +SharedFD &SharedFD::operator=(const SharedFD &other)\n> > > +{\n> > > +\tfd_ = other.fd_;\n> > > +\n> > > +\treturn *this;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\brief Move assignment operator, replace the wrapped file descriptor by\n> > > + * taking over \\a other\n> > > + * \\param[in] other The other SharedFD\n> > > + *\n> > > + * Moving a SharedFD moves the reference to the wrapped descriptor owned by\n> > > + * \\a other to the new SharedFD. If \\a other is invalid, *this will also be\n> > > + * invalid. The \\a other SharedFD is invalidated and its fd() function will\n> > > + * return -1. The wrapped file descriptor will be closed automatically when\n> > > + * all SharedFD instances that reference it are destroyed.\n> > > + *\n> > > + * \\return A reference to this SharedFD\n> > > + */\n> > > +SharedFD &SharedFD::operator=(SharedFD &&other)\n> > > +{\n> > > +\tfd_ = std::move(other.fd_);\n> > > +\n> > > +\treturn *this;\n> > > +}\n> > > +\n> > > +/**\n> > > + * \\fn SharedFD::isValid()\n> > > + * \\brief Check if the SharedFD instance is valid\n> > > + * \\return True if the SharedFD is valid, false otherwise\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\fn SharedFD::fd()\n> > > + * \\brief Retrieve the numerical file descriptor\n> > > + * \\return The numerical file descriptor, which may be -1 if the SharedFD\n> > > + * instance is invalid\n> > > + */\n> > > +\n> > > +/**\n> > > + * \\brief Duplicate a SharedFD\n> > > + *\n> > > + * Duplicating a SharedFD creates a duplicate of the wrapped file descriptor and\n> > > + * returns a UniqueFD that owns the duplicate. The fd() function of the original\n> > > + * and the get() function of the duplicate will return different values. The\n> > > + * duplicate instance will not be affected by destruction of the original\n> > > + * instance or its copies.\n> > > + *\n> > > + * \\return A UniqueFD owning a duplicate of the original file descriptor\n> > > + */\n> > > +UniqueFD SharedFD::dup() const\n> > > +{\n> > > +\tint dupFd = ::dup(fd());\n> > > +\tif (dupFd == -1) {\n> > > +\t\tint ret = -errno;\n> > > +\t\tLOG(SharedFD, Error)\n> > > +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > > +\t}\n> > > +\n> > > +\treturn UniqueFD(dupFd);\n> > > +}\n> > > +\n> > > +SharedFD::Descriptor::Descriptor(int fd, bool duplicate)\n> > > +{\n> > > +\tif (!duplicate) {\n> > > +\t\tfd_ = fd;\n> > > +\t\treturn;\n> > > +\t}\n> > > +\n> > > +\t/* Failing to dup() a fd should not happen and is fatal. */\n> > > +\tfd_ = ::dup(fd);\n> > > +\tif (fd_ == -1) {\n> > > +\t\tint ret = -errno;\n> > > +\t\tLOG(SharedFD, Fatal)\n> > > +\t\t\t<< \"Failed to dup() fd: \" << strerror(-ret);\n> > > +\t}\n> > > +}\n> > > +\n> > > +SharedFD::Descriptor::~Descriptor()\n> > > +{\n> > > +\tif (fd_ != -1)\n> > > +\t\tclose(fd_);\n> > > +}\n> > > +\n> > > +} /* namespace libcamera */\n> > > diff --git a/src/libcamera/framebuffer.cpp b/src/libcamera/framebuffer.cpp\n> > > index f5bcf107d7aa..0a5bf7fdbeb7 100644\n> > > --- a/src/libcamera/framebuffer.cpp\n> > > +++ b/src/libcamera/framebuffer.cpp\n> > > @@ -180,9 +180,9 @@ FrameBuffer::Private::Private()\n> > >   * offset and length.\n> > >   *\n> > >   * To support DMA access, planes are associated with dmabuf objects represented\n> > > - * by FileDescriptor handles. The Plane class doesn't handle mapping of the\n> > > - * memory to the CPU, but applications and IPAs may use the dmabuf file\n> > > - * descriptors to map the plane memory with mmap() and access its contents.\n> > > + * by SharedFD handles. The Plane class doesn't handle mapping of the memory to\n> > > + * the CPU, but applications and IPAs may use the dmabuf file descriptors to map\n> > > + * the plane memory with mmap() and access its contents.\n> > >   *\n> > >   * \\todo Specify how an application shall decide whether to use a single or\n> > >   * multiple dmabufs, based on the camera requirements.\n> > > diff --git a/src/libcamera/ipa_data_serializer.cpp b/src/libcamera/ipa_data_serializer.cpp\n> > > index 82ec9b20a411..0a259305afa2 100644\n> > > --- a/src/libcamera/ipa_data_serializer.cpp\n> > > +++ b/src/libcamera/ipa_data_serializer.cpp\n> > > @@ -31,7 +31,7 @@ LOG_DEFINE_CATEGORY(IPADataSerializer)\n> > >   *\n> > >   * \\todo Harden the vector and map deserializer\n> > >   *\n> > > - * \\todo For FileDescriptors, instead of storing a validity flag, store an\n> > > + * \\todo For SharedFDs, instead of storing a validity flag, store an\n> > >   * index into the fd array. This will allow us to use views instead of copying.\n> > >   */\n> > >\n> > > @@ -112,7 +112,7 @@ namespace {\n> > >   * \\param[in] cs ControlSerializer\n> > >   *\n> > >   * This version of deserialize() can be used if the object type \\a T and its\n> > > - * members don't have any FileDescriptor.\n> > > + * members don't have any SharedFD.\n> > >   *\n> > >   * \\a cs is only necessary if the object type \\a T or its members contain\n> > >   * ControlList or ControlInfoMap.\n> > > @@ -132,7 +132,7 @@ namespace {\n> > >   * \\param[in] cs ControlSerializer\n> > >   *\n> > >   * This version of deserialize() can be used if the object type \\a T and its\n> > > - * members don't have any FileDescriptor.\n> > > + * members don't have any SharedFD.\n> > >   *\n> > >   * \\a cs is only necessary if the object type \\a T or its members contain\n> > >   * ControlList or ControlInfoMap.\n> > > @@ -143,7 +143,7 @@ namespace {\n> > >  /**\n> > >   * \\fn template<typename T> IPADataSerializer<T>::deserialize(\n> > >   * \tconst std::vector<uint8_t> &data,\n> > > - * \tconst std::vector<FileDescriptor> &fds,\n> > > + * \tconst std::vector<SharedFD> &fds,\n> > >   * \tControlSerializer *cs = nullptr)\n> > >   * \\brief Deserialize byte vector and fd vector into an object\n> > >   * \\tparam T Type of object to deserialize to\n> > > @@ -152,7 +152,7 @@ namespace {\n> > >   * \\param[in] cs ControlSerializer\n> > >   *\n> > >   * This version of deserialize() (or the iterator version) must be used if\n> > > - * the object type \\a T or its members contain FileDescriptor.\n> > > + * the object type \\a T or its members contain SharedFD.\n> > >   *\n> > >   * \\a cs is only necessary if the object type \\a T or its members contain\n> > >   * ControlList or ControlInfoMap.\n> > > @@ -164,8 +164,8 @@ namespace {\n> > >   * \\fn template<typename T> IPADataSerializer::deserialize(\n> > >   * \tstd::vector<uint8_t>::const_iterator dataBegin,\n> > >   * \tstd::vector<uint8_t>::const_iterator dataEnd,\n> > > - * \tstd::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > - * \tstd::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > + * \tstd::vector<SharedFD>::const_iterator fdsBegin,\n> > > + * \tstd::vector<SharedFD>::const_iterator fdsEnd,\n> > >   * \tControlSerializer *cs = nullptr)\n> > >   * \\brief Deserialize byte vector and fd vector into an object\n> > >   * \\tparam T Type of object to deserialize to\n> > > @@ -176,7 +176,7 @@ namespace {\n> > >   * \\param[in] cs ControlSerializer\n> > >   *\n> > >   * This version of deserialize() (or the vector version) must be used if\n> > > - * the object type \\a T or its members contain FileDescriptor.\n> > > + * the object type \\a T or its members contain SharedFD.\n> > >   *\n> > >   * \\a cs is only necessary if the object type \\a T or its members contain\n> > >   * ControlList or ControlInfoMap.\n> > > @@ -189,7 +189,7 @@ namespace {\n> > >  #define DEFINE_POD_SERIALIZER(type)\t\t\t\t\t\\\n> > >  \t\t\t\t\t\t\t\t\t\\\n> > >  template<>\t\t\t\t\t\t\t\t\\\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\t\t\\\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\t\t\\\n> > >  IPADataSerializer<type>::serialize(const type &data,\t\t\t\\\n> > >  \t\t\t\t  [[maybe_unused]] ControlSerializer *cs) \\\n> > >  {\t\t\t\t\t\t\t\t\t\\\n> > > @@ -217,7 +217,7 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> > >  \t\t\t\t\t\t\t\t\t\\\n> > >  template<>\t\t\t\t\t\t\t\t\\\n> > >  type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> > > -\t\t\t\t\t  [[maybe_unused]] const std::vector<FileDescriptor> &fds, \\\n> > > +\t\t\t\t\t  [[maybe_unused]] const std::vector<SharedFD> &fds, \\\n> > >  \t\t\t\t\t  ControlSerializer *cs)\t\\\n> > >  {\t\t\t\t\t\t\t\t\t\\\n> > >  \treturn deserialize(data.cbegin(), data.end(), cs);\t\t\\\n> > > @@ -226,8 +226,8 @@ type IPADataSerializer<type>::deserialize(const std::vector<uint8_t> &data, \\\n> > >  template<>\t\t\t\t\t\t\t\t\\\n> > >  type IPADataSerializer<type>::deserialize(std::vector<uint8_t>::const_iterator dataBegin, \\\n> > >  \t\t\t\t\t  std::vector<uint8_t>::const_iterator dataEnd, \\\n> > > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin, \\\n> > > -\t\t\t\t\t  [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd, \\\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin, \\\n> > > +\t\t\t\t\t  [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd, \\\n> > >  \t\t\t\t\t  ControlSerializer *cs)\t\\\n> > >  {\t\t\t\t\t\t\t\t\t\\\n> > >  \treturn deserialize(dataBegin, dataEnd, cs);\t\t\t\\\n> > > @@ -251,7 +251,7 @@ DEFINE_POD_SERIALIZER(double)\n> > >   * function parameter serdes).\n> > >   */\n> > >  template<>\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  IPADataSerializer<std::string>::serialize(const std::string &data,\n> > >  \t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > > @@ -278,7 +278,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n> > >  template<>\n> > >  std::string\n> > >  IPADataSerializer<std::string>::deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > > +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \treturn { data.cbegin(), data.cend() };\n> > > @@ -288,8 +288,8 @@ template<>\n> > >  std::string\n> > >  IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t    [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \treturn { dataBegin, dataEnd };\n> > > @@ -307,7 +307,7 @@ IPADataSerializer<std::string>::deserialize(std::vector<uint8_t>::const_iterator\n> > >   * be used. The serialized ControlInfoMap will have zero length.\n> > >   */\n> > >  template<>\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  IPADataSerializer<ControlList>::serialize(const ControlList &data, ControlSerializer *cs)\n> > >  {\n> > >  \tif (!cs)\n> > > @@ -407,7 +407,7 @@ IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> > >  template<>\n> > >  ControlList\n> > >  IPADataSerializer<ControlList>::deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t\t\t    [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > > +\t\t\t\t\t    [[maybe_unused]] const std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t    ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(data.cbegin(), data.end(), cs);\n> > > @@ -417,8 +417,8 @@ template<>\n> > >  ControlList\n> > >  IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t    ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(dataBegin, dataEnd, cs);\n> > > @@ -431,7 +431,7 @@ IPADataSerializer<ControlList>::deserialize(std::vector<uint8_t>::const_iterator\n> > >   * X bytes - Serialized ControlInfoMap (using ControlSerializer)\n> > >   */\n> > >  template<>\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  IPADataSerializer<ControlInfoMap>::serialize(const ControlInfoMap &map,\n> > >  \t\t\t\t\t     ControlSerializer *cs)\n> > >  {\n> > > @@ -493,7 +493,7 @@ IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> > >  template<>\n> > >  ControlInfoMap\n> > >  IPADataSerializer<ControlInfoMap>::deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t\t\t       [[maybe_unused]] const std::vector<FileDescriptor> &fds,\n> > > +\t\t\t\t\t       [[maybe_unused]] const std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t       ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(data.cbegin(), data.end(), cs);\n> > > @@ -503,30 +503,30 @@ template<>\n> > >  ControlInfoMap\n> > >  IPADataSerializer<ControlInfoMap>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t       std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t       [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t       [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t       ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(dataBegin, dataEnd, cs);\n> > >  }\n> > >\n> > >  /*\n> > > - * FileDescriptors are serialized into four bytes that tells if the\n> > > - * FileDescriptor is valid or not. If it is valid, then for serialization\n> > > - * the fd will be written to the fd vector, or for deserialization the\n> > > - * fd vector const_iterator will be valid.\n> > > + * SharedFD instances are serialized into four bytes that tells if the SharedFD\n> > > + * is valid or not. If it is valid, then for serialization the fd will be\n> > > + * written to the fd vector, or for deserialization the fd vector const_iterator\n> > > + * will be valid.\n> > >   *\n> > >   * This validity is necessary so that we don't send -1 fd over sendmsg(). It\n> > >   * also allows us to simply send the entire fd vector into the deserializer\n> > >   * and it will be recursively consumed as necessary.\n> > >   */\n> > >  template<>\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > -IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> > > -\t\t\t\t\t     [[maybe_unused]] ControlSerializer *cs)\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > > +IPADataSerializer<SharedFD>::serialize(const SharedFD &data,\n> > > +\t\t\t\t       [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \tstd::vector<uint8_t> dataVec;\n> > > -\tstd::vector<FileDescriptor> fdVec;\n> > > +\tstd::vector<SharedFD> fdVec;\n> > >\n> > >  \t/*\n> > >  \t * Store as uint32_t to prepare for conversion from validity flag\n> > > @@ -542,11 +542,11 @@ IPADataSerializer<FileDescriptor>::serialize(const FileDescriptor &data,\n> > >  }\n> > >\n> > >  template<>\n> > > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> > > -\t\t\t\t\t\t\t      [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t\t\t      std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> > > +SharedFD IPADataSerializer<SharedFD>::deserialize([[maybe_unused]] std::vector<uint8_t>::const_iterator dataBegin,\n> > > +\t\t\t\t\t\t  [[maybe_unused]] std::vector<uint8_t>::const_iterator dataEnd,\n> > > +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t\t  std::vector<SharedFD>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \tASSERT(std::distance(dataBegin, dataEnd) >= 4);\n> > >\n> > > @@ -554,13 +554,13 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize([[maybe_unused]] s\n> > >\n> > >  \tASSERT(!(valid && std::distance(fdsBegin, fdsEnd) < 1));\n> > >\n> > > -\treturn valid ? *fdsBegin : FileDescriptor();\n> > > +\treturn valid ? *fdsBegin : SharedFD();\n> > >  }\n> > >\n> > >  template<>\n> > > -FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t\t\t\t\t      const std::vector<FileDescriptor> &fds,\n> > > -\t\t\t\t\t\t\t      [[maybe_unused]] ControlSerializer *cs)\n> > > +SharedFD IPADataSerializer<SharedFD>::deserialize(const std::vector<uint8_t> &data,\n> > > +\t\t\t\t\t\t  const std::vector<SharedFD> &fds,\n> > > +\t\t\t\t\t\t  [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end());\n> > >  }\n> > > @@ -568,22 +568,22 @@ FileDescriptor IPADataSerializer<FileDescriptor>::deserialize(const std::vector<\n> > >  /*\n> > >   * FrameBuffer::Plane is serialized as:\n> > >   *\n> > > - * 4 byte  - FileDescriptor\n> > > + * 4 byte  - SharedFD\n> > >   * 4 bytes - uint32_t Offset\n> > >   * 4 bytes - uint32_t Length\n> > >   */\n> > >  template<>\n> > > -std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  IPADataSerializer<FrameBuffer::Plane>::serialize(const FrameBuffer::Plane &data,\n> > >  \t\t\t\t\t\t [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \tstd::vector<uint8_t> dataVec;\n> > > -\tstd::vector<FileDescriptor> fdsVec;\n> > > +\tstd::vector<SharedFD> fdsVec;\n> > >\n> > >  \tstd::vector<uint8_t> fdBuf;\n> > > -\tstd::vector<FileDescriptor> fdFds;\n> > > +\tstd::vector<SharedFD> fdFds;\n> > >  \tstd::tie(fdBuf, fdFds) =\n> > > -\t\tIPADataSerializer<FileDescriptor>::serialize(data.fd);\n> > > +\t\tIPADataSerializer<SharedFD>::serialize(data.fd);\n> > >  \tdataVec.insert(dataVec.end(), fdBuf.begin(), fdBuf.end());\n> > >  \tfdsVec.insert(fdsVec.end(), fdFds.begin(), fdFds.end());\n> > >\n> > > @@ -597,13 +597,13 @@ template<>\n> > >  FrameBuffer::Plane\n> > >  IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t\t\t\t\t   std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t\t\t\t\t   std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t\t\t\t\t   [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t\t\t\t\t   std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t\t\t\t\t   [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t\t\t\t\t   [[maybe_unused]] ControlSerializer *cs)\n> > >  {\n> > >  \tFrameBuffer::Plane ret;\n> > >\n> > > -\tret.fd = IPADataSerializer<FileDescriptor>::deserialize(dataBegin, dataBegin + 4,\n> > > +\tret.fd = IPADataSerializer<SharedFD>::deserialize(dataBegin, dataBegin + 4,\n> > >  \t\t\t\t\t\t\t\tfdsBegin, fdsBegin + 1);\n> > >  \tret.offset = readPOD<uint32_t>(dataBegin, 4, dataEnd);\n> > >  \tret.length = readPOD<uint32_t>(dataBegin, 8, dataEnd);\n> > > @@ -614,7 +614,7 @@ IPADataSerializer<FrameBuffer::Plane>::deserialize(std::vector<uint8_t>::const_i\n> > >  template<>\n> > >  FrameBuffer::Plane\n> > >  IPADataSerializer<FrameBuffer::Plane>::deserialize(const std::vector<uint8_t> &data,\n> > > -\t\t\t\t\t\t   const std::vector<FileDescriptor> &fds,\n> > > +\t\t\t\t\t\t   const std::vector<SharedFD> &fds,\n> > >  \t\t\t\t\t\t   ControlSerializer *cs)\n> > >  {\n> > >  \treturn deserialize(data.cbegin(), data.end(), fds.cbegin(), fds.end(), cs);\n> > > diff --git a/src/libcamera/ipc_pipe.cpp b/src/libcamera/ipc_pipe.cpp\n> > > index ad870fd4137f..3b47032de0a2 100644\n> > > --- a/src/libcamera/ipc_pipe.cpp\n> > > +++ b/src/libcamera/ipc_pipe.cpp\n> > > @@ -87,7 +87,7 @@ IPCMessage::IPCMessage(IPCUnixSocket::Payload &payload)\n> > >  \tdata_ = std::vector<uint8_t>(payload.data.begin() + sizeof(header_),\n> > >  \t\t\t\t     payload.data.end());\n> > >  \tfor (int32_t &fd : payload.fds)\n> > > -\t\tfds_.push_back(FileDescriptor(std::move(fd)));\n> > > +\t\tfds_.push_back(SharedFD(std::move(fd)));\n> > >  }\n> > >\n> > >  /**\n> > > @@ -112,7 +112,7 @@ IPCUnixSocket::Payload IPCMessage::payload() const\n> > >  \t\t       data_.data(), data_.size());\n> > >  \t}\n> > >\n> > > -\tfor (const FileDescriptor &fd : fds_)\n> > > +\tfor (const SharedFD &fd : fds_)\n> > >  \t\tpayload.fds.push_back(fd.fd());\n> > >\n> > >  \treturn payload;\n> > > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > > index ffa51a0c65ca..ea8243912a29 100644\n> > > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n> > > @@ -12,7 +12,7 @@\n> > >  #include <queue>\n> > >  #include <unordered_set>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >\n> > >  #include <libcamera/camera.h>\n> > >  #include <libcamera/control_ids.h>\n> > > @@ -228,7 +228,7 @@ public:\n> > >\n> > >  \t/* DMAHEAP allocation helper. */\n> > >  \tRPi::DmaHeap dmaHeap_;\n> > > -\tFileDescriptor lsTable_;\n> > > +\tSharedFD lsTable_;\n> > >\n> > >  \tstd::unique_ptr<DelayedControls> delayedCtrls_;\n> > >  \tbool sensorMetadata_;\n> > > @@ -1365,7 +1365,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config)\n> > >  \t\tif (!fd.isValid())\n> > >  \t\t\treturn -ENOMEM;\n> > >\n> > > -\t\tlsTable_ = FileDescriptor(std::move(fd));\n> > > +\t\tlsTable_ = SharedFD(std::move(fd));\n> > >\n> > >  \t\t/* Allow the IPA to mmap the LS table via the file descriptor. */\n> > >  \t\t/*\n> > > diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp\n> > > index 3966483a365f..97d431071def 100644\n> > > --- a/src/libcamera/v4l2_videodevice.cpp\n> > > +++ b/src/libcamera/v4l2_videodevice.cpp\n> > > @@ -22,8 +22,8 @@\n> > >  #include <linux/version.h>\n> > >\n> > >  #include <libcamera/base/event_notifier.h>\n> > > -#include <libcamera/base/file_descriptor.h>\n> > >  #include <libcamera/base/log.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >  #include <libcamera/base/unique_fd.h>\n> > >  #include <libcamera/base/utils.h>\n> > >\n> > > @@ -1325,7 +1325,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> > >  \t\t\treturn nullptr;\n> > >\n> > >  \t\tFrameBuffer::Plane plane;\n> > > -\t\tplane.fd = FileDescriptor(std::move(fd));\n> > > +\t\tplane.fd = SharedFD(std::move(fd));\n> > >  \t\t/*\n> > >  \t\t * V4L2 API doesn't provide dmabuf offset information of plane.\n> > >  \t\t * Set 0 as a placeholder offset.\n> > > @@ -1354,7 +1354,7 @@ std::unique_ptr<FrameBuffer> V4L2VideoDevice::createBuffer(unsigned int index)\n> > >  \t\tASSERT(numPlanes == 1u);\n> > >\n> > >  \t\tplanes.resize(formatInfo_->numPlanes());\n> > > -\t\tconst FileDescriptor &fd = planes[0].fd;\n> > > +\t\tconst SharedFD &fd = planes[0].fd;\n> > >  \t\tsize_t offset = 0;\n> > >\n> > >  \t\tfor (auto [i, plane] : utils::enumerate(planes)) {\n> > > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h\n> > > index 9817fd393d59..586347829845 100644\n> > > --- a/src/v4l2/v4l2_camera.h\n> > > +++ b/src/v4l2/v4l2_camera.h\n> > > @@ -11,8 +11,8 @@\n> > >  #include <mutex>\n> > >  #include <utility>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > >  #include <libcamera/base/semaphore.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >\n> > >  #include <libcamera/camera.h>\n> > >  #include <libcamera/framebuffer.h>\n> > > diff --git a/test/meson.build b/test/meson.build\n> > > index 42dfbc1f8ee9..daaa3862cdd6 100644\n> > > --- a/test/meson.build\n> > > +++ b/test/meson.build\n> > > @@ -40,7 +40,6 @@ internal_tests = [\n> > >      ['event-dispatcher',                'event-dispatcher.cpp'],\n> > >      ['event-thread',                    'event-thread.cpp'],\n> > >      ['file',                            'file.cpp'],\n> > > -    ['file-descriptor',                 'file-descriptor.cpp'],\n> > >      ['flags',                           'flags.cpp'],\n> > >      ['hotplug-cameras',                 'hotplug-cameras.cpp'],\n> > >      ['mapped-buffer',                   'mapped-buffer.cpp'],\n> > > @@ -49,6 +48,7 @@ internal_tests = [\n> > >      ['object-delete',                   'object-delete.cpp'],\n> > >      ['object-invoke',                   'object-invoke.cpp'],\n> > >      ['pixel-format',                    'pixel-format.cpp'],\n> > > +    ['shared-fd',                       'shared-fd.cpp'],\n> > >      ['signal-threads',                  'signal-threads.cpp'],\n> > >      ['threads',                         'threads.cpp'],\n> > >      ['timer',                           'timer.cpp'],\n> > > diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp\n> > > index 5fcdcb8eae92..d2050a868b38 100644\n> > > --- a/test/serialization/ipa_data_serializer_test.cpp\n> > > +++ b/test/serialization/ipa_data_serializer_test.cpp\n> > > @@ -53,7 +53,7 @@ template<typename T>\n> > >  int testPodSerdes(T in)\n> > >  {\n> > >  \tstd::vector<uint8_t> buf;\n> > > -\tstd::vector<FileDescriptor> fds;\n> > > +\tstd::vector<SharedFD> fds;\n> > >\n> > >  \tstd::tie(buf, fds) = IPADataSerializer<T>::serialize(in);\n> > >  \tT out = IPADataSerializer<T>::deserialize(buf, fds);\n> > > @@ -72,7 +72,7 @@ int testVectorSerdes(const std::vector<T> &in,\n> > >  \t\t     ControlSerializer *cs = nullptr)\n> > >  {\n> > >  \tstd::vector<uint8_t> buf;\n> > > -\tstd::vector<FileDescriptor> fds;\n> > > +\tstd::vector<SharedFD> fds;\n> > >\n> > >  \tstd::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);\n> > >  \tstd::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);\n> > > @@ -92,7 +92,7 @@ int testMapSerdes(const std::map<K, V> &in,\n> > >  \t\t  ControlSerializer *cs = nullptr)\n> > >  {\n> > >  \tstd::vector<uint8_t> buf;\n> > > -\tstd::vector<FileDescriptor> fds;\n> > > +\tstd::vector<SharedFD> fds;\n> > >\n> > >  \tstd::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);\n> > >  \tstd::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);\n> > > @@ -198,7 +198,7 @@ private:\n> > >  \t\tControlSerializer cs(ControlSerializer::Role::Proxy);\n> > >\n> > >  \t\t/*\n> > > -\t\t * We don't test FileDescriptor serdes because it dup()s, so we\n> > > +\t\t * We don't test SharedFD serdes because it dup()s, so we\n> > >  \t\t * can't check for equality.\n> > >  \t\t */\n> > >  \t\tstd::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };\n> > > @@ -219,7 +219,7 @@ private:\n> > >  \t\t};\n> > >\n> > >  \t\tstd::vector<uint8_t> buf;\n> > > -\t\tstd::vector<FileDescriptor> fds;\n> > > +\t\tstd::vector<SharedFD> fds;\n> > >\n> > >  \t\tif (testVectorSerdes(vecUint8) != TestPass)\n> > >  \t\t\treturn TestFail;\n> > > @@ -291,7 +291,7 @@ private:\n> > >  \t\t\t{ { \"a\", { 1, 2, 3 } }, { \"b\", { 4, 5, 6 } }, { \"c\", { 7, 8, 9 } } };\n> > >\n> > >  \t\tstd::vector<uint8_t> buf;\n> > > -\t\tstd::vector<FileDescriptor> fds;\n> > > +\t\tstd::vector<SharedFD> fds;\n> > >\n> > >  \t\tif (testMapSerdes(mapUintStr) != TestPass)\n> > >  \t\t\treturn TestFail;\n> > > @@ -359,7 +359,7 @@ private:\n> > >  \t\tstd::string strEmpty = \"\";\n> > >\n> > >  \t\tstd::vector<uint8_t> buf;\n> > > -\t\tstd::vector<FileDescriptor> fds;\n> > > +\t\tstd::vector<SharedFD> fds;\n> > >\n> > >  \t\tif (testPodSerdes(u32min) != TestPass)\n> > >  \t\t\treturn TestFail;\n> > > diff --git a/test/file-descriptor.cpp b/test/shared-fd.cpp\n> > > similarity index 80%\n> > > rename from test/file-descriptor.cpp\n> > > rename to test/shared-fd.cpp\n> > > index 76badc4c5fad..60e5d0aaa395 100644\n> > > --- a/test/file-descriptor.cpp\n> > > +++ b/test/shared-fd.cpp\n> > > @@ -2,7 +2,7 @@\n> > >  /*\n> > >   * Copyright (C) 2019, Google Inc.\n> > >   *\n> > > - * file_descriptor.cpp - FileDescriptor test\n> > > + * shared_fd.cpp - SharedFD test\n> > >   */\n> > >\n> > >  #include <fcntl.h>\n> > > @@ -11,7 +11,7 @@\n> > >  #include <sys/types.h>\n> > >  #include <unistd.h>\n> > >\n> > > -#include <libcamera/base/file_descriptor.h>\n> > > +#include <libcamera/base/shared_fd.h>\n> > >  #include <libcamera/base/utils.h>\n> > >\n> > >  #include \"test.h\"\n> > > @@ -19,7 +19,7 @@\n> > >  using namespace libcamera;\n> > >  using namespace std;\n> > >\n> > > -class FileDescriptorTest : public Test\n> > > +class SharedFDTest : public Test\n> > >  {\n> > >  protected:\n> > >  \tint init()\n> > > @@ -43,8 +43,8 @@ protected:\n> > >\n> > >  \tint run()\n> > >  \t{\n> > > -\t\t/* Test creating empty FileDescriptor. */\n> > > -\t\tdesc1_ = new FileDescriptor();\n> > > +\t\t/* Test creating empty SharedFD. */\n> > > +\t\tdesc1_ = new SharedFD();\n> > >\n> > >  \t\tif (desc1_->fd() != -1) {\n> > >  \t\t\tstd::cout << \"Failed fd numerical check (default constructor)\"\n> > > @@ -56,10 +56,10 @@ protected:\n> > >  \t\tdesc1_ = nullptr;\n> > >\n> > >  \t\t/*\n> > > -\t\t * Test creating FileDescriptor by copying numerical file\n> > > +\t\t * Test creating SharedFD by copying numerical file\n> > >  \t\t * descriptor.\n> > >  \t\t */\n> > > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > > +\t\tdesc1_ = new SharedFD(fd_);\n> > >  \t\tif (desc1_->fd() == fd_) {\n> > >  \t\t\tstd::cout << \"Failed fd numerical check (lvalue ref constructor)\"\n> > >  \t\t\t\t  << std::endl;\n> > > @@ -84,13 +84,13 @@ protected:\n> > >  \t\t}\n> > >\n> > >  \t\t/*\n> > > -\t\t * Test creating FileDescriptor by taking ownership of\n> > > +\t\t * Test creating SharedFD by taking ownership of\n> > >  \t\t * numerical file descriptor.\n> > >  \t\t */\n> > >  \t\tint dupFd = dup(fd_);\n> > >  \t\tint dupFdCopy = dupFd;\n> > >\n> > > -\t\tdesc1_ = new FileDescriptor(std::move(dupFd));\n> > > +\t\tdesc1_ = new SharedFD(std::move(dupFd));\n> > >  \t\tif (desc1_->fd() != dupFdCopy) {\n> > >  \t\t\tstd::cout << \"Failed fd numerical check (rvalue ref constructor)\"\n> > >  \t\t\t\t  << std::endl;\n> > > @@ -114,9 +114,9 @@ protected:\n> > >  \t\t\treturn TestFail;\n> > >  \t\t}\n> > >\n> > > -\t\t/* Test creating FileDescriptor from other FileDescriptor. */\n> > > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > > -\t\tdesc2_ = new FileDescriptor(*desc1_);\n> > > +\t\t/* Test creating SharedFD from other SharedFD. */\n> > > +\t\tdesc1_ = new SharedFD(fd_);\n> > > +\t\tdesc2_ = new SharedFD(*desc1_);\n> > >\n> > >  \t\tif (desc1_->fd() == fd_ || desc2_->fd() == fd_ || desc1_->fd() != desc2_->fd()) {\n> > >  \t\t\tstd::cout << \"Failed fd numerical check (copy constructor)\"\n> > > @@ -142,10 +142,10 @@ protected:\n> > >  \t\tdelete desc2_;\n> > >  \t\tdesc2_ = nullptr;\n> > >\n> > > -\t\t/* Test creating FileDescriptor by taking over other FileDescriptor. */\n> > > -\t\tdesc1_ = new FileDescriptor(fd_);\n> > > +\t\t/* Test creating SharedFD by taking over other SharedFD. */\n> > > +\t\tdesc1_ = new SharedFD(fd_);\n> > >  \t\tfd = desc1_->fd();\n> > > -\t\tdesc2_ = new FileDescriptor(std::move(*desc1_));\n> > > +\t\tdesc2_ = new SharedFD(std::move(*desc1_));\n> > >\n> > >  \t\tif (desc1_->fd() != -1 || desc2_->fd() != fd) {\n> > >  \t\t\tstd::cout << \"Failed fd numerical check (move constructor)\"\n> > > @@ -164,9 +164,9 @@ protected:\n> > >  \t\tdelete desc2_;\n> > >  \t\tdesc2_ = nullptr;\n> > >\n> > > -\t\t/* Test creating FileDescriptor by copy assignment. */\n> > > -\t\tdesc1_ = new FileDescriptor();\n> > > -\t\tdesc2_ = new FileDescriptor(fd_);\n> > > +\t\t/* Test creating SharedFD by copy assignment. */\n> > > +\t\tdesc1_ = new SharedFD();\n> > > +\t\tdesc2_ = new SharedFD(fd_);\n> > >\n> > >  \t\tfd = desc2_->fd();\n> > >  \t\t*desc1_ = *desc2_;\n> > > @@ -188,9 +188,9 @@ protected:\n> > >  \t\tdelete desc2_;\n> > >  \t\tdesc2_ = nullptr;\n> > >\n> > > -\t\t/* Test creating FileDescriptor by move assignment. */\n> > > -\t\tdesc1_ = new FileDescriptor();\n> > > -\t\tdesc2_ = new FileDescriptor(fd_);\n> > > +\t\t/* Test creating SharedFD by move assignment. */\n> > > +\t\tdesc1_ = new SharedFD();\n> > > +\t\tdesc2_ = new SharedFD(fd_);\n> > >\n> > >  \t\tfd = desc2_->fd();\n> > >  \t\t*desc1_ = std::move(*desc2_);\n> > > @@ -237,7 +237,7 @@ private:\n> > >\n> > >  \tint fd_;\n> > >  \tino_t inodeNr_;\n> > > -\tFileDescriptor *desc1_, *desc2_;\n> > > +\tSharedFD *desc1_, *desc2_;\n> > >  };\n> > >\n> > > -TEST_REGISTER(FileDescriptorTest)\n> > > +TEST_REGISTER(SharedFDTest)\n> > > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > > index d856339aa9ee..c37c4941b528 100644\n> > > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.cpp.tmpl\n> > > @@ -237,7 +237,7 @@ void {{proxy_name}}::recvMessage(const IPCMessage &data)\n> > >  void {{proxy_name}}::{{method.mojom_name}}IPC(\n> > >  \tstd::vector<uint8_t>::const_iterator data,\n> > >  \tsize_t dataSize,\n> > > -\t[[maybe_unused]] const std::vector<FileDescriptor> &fds)\n> > > +\t[[maybe_unused]] const std::vector<SharedFD> &fds)\n> > >  {\n> > >  {%- for param in method.parameters %}\n> > >  \t{{param|name}} {{param.mojom_name}};\n> > > diff --git a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > > index ce396c183d0c..c308dd10c7e5 100644\n> > > --- a/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > > +++ b/utils/ipc/generators/libcamera_templates/module_ipa_proxy.h.tmpl\n> > > @@ -64,7 +64,7 @@ private:\n> > >  \tvoid {{method.mojom_name}}IPC(\n> > >  \t\tstd::vector<uint8_t>::const_iterator data,\n> > >  \t\tsize_t dataSize,\n> > > -\t\tconst std::vector<FileDescriptor> &fds);\n> > > +\t\tconst std::vector<SharedFD> &fds);\n> > >  {% endfor %}\n> > >\n> > >  \t/* Helper class to invoke async functions in another thread. */\n> > > diff --git a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > > index ebcd2aaaafae..bac826a74c2d 100644\n> > > --- a/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > > +++ b/utils/ipc/generators/libcamera_templates/proxy_functions.tmpl\n> > > @@ -54,7 +54,7 @@\n> > >  {%- for param in params %}\n> > >  \tstd::vector<uint8_t> {{param.mojom_name}}Buf;\n> > >  {%- if param|has_fd %}\n> > > -\tstd::vector<FileDescriptor> {{param.mojom_name}}Fds;\n> > > +\tstd::vector<SharedFD> {{param.mojom_name}}Fds;\n> > >  \tstd::tie({{param.mojom_name}}Buf, {{param.mojom_name}}Fds) =\n> > >  {%- else %}\n> > >  \tstd::tie({{param.mojom_name}}Buf, std::ignore) =\n> > > diff --git a/utils/ipc/generators/libcamera_templates/serializer.tmpl b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > > index b8ef8e7b974e..77bae36fe6b7 100644\n> > > --- a/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > > +++ b/utils/ipc/generators/libcamera_templates/serializer.tmpl\n> > > @@ -40,7 +40,7 @@\n> > >  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> > >  {%- elif field|is_fd %}\n> > >  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n> > > -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> > > +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n> > >  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n> > >  \t\t\tIPADataSerializer<{{field|name}}>::serialize(data.{{field.mojom_name}});\n> > >  \t\tretData.insert(retData.end(), {{field.mojom_name}}.begin(), {{field.mojom_name}}.end());\n> > > @@ -58,7 +58,7 @@\n> > >  {%- elif field|is_plain_struct or field|is_array or field|is_map or field|is_str %}\n> > >  \t\tstd::vector<uint8_t> {{field.mojom_name}};\n> > >  \t{%- if field|has_fd %}\n> > > -\t\tstd::vector<FileDescriptor> {{field.mojom_name}}Fds;\n> > > +\t\tstd::vector<SharedFD> {{field.mojom_name}}Fds;\n> > >  \t\tstd::tie({{field.mojom_name}}, {{field.mojom_name}}Fds) =\n> > >  \t{%- else %}\n> > >  \t\tstd::tie({{field.mojom_name}}, std::ignore) =\n> > > @@ -177,7 +177,7 @@\n> > >   # \\a struct.\n> > >   #}\n> > >  {%- macro serializer(struct, namespace) %}\n> > > -\tstatic std::tuple<std::vector<uint8_t>, std::vector<FileDescriptor>>\n> > > +\tstatic std::tuple<std::vector<uint8_t>, std::vector<SharedFD>>\n> > >  \tserialize(const {{struct|name_full}} &data,\n> > >  {%- if struct|needs_control_serializer %}\n> > >  \t\t  ControlSerializer *cs)\n> > > @@ -187,7 +187,7 @@\n> > >  \t{\n> > >  \t\tstd::vector<uint8_t> retData;\n> > >  {%- if struct|has_fd %}\n> > > -\t\tstd::vector<FileDescriptor> retFds;\n> > > +\t\tstd::vector<SharedFD> retFds;\n> > >  {%- endif %}\n> > >  {%- for field in struct.fields %}\n> > >  {{serializer_field(field, namespace, loop)}}\n> > > @@ -210,7 +210,7 @@\n> > >  {%- macro deserializer_fd(struct, namespace) %}\n> > >  \tstatic {{struct|name_full}}\n> > >  \tdeserialize(std::vector<uint8_t> &data,\n> > > -\t\t    std::vector<FileDescriptor> &fds,\n> > > +\t\t    std::vector<SharedFD> &fds,\n> > >  {%- if struct|needs_control_serializer %}\n> > >  \t\t    ControlSerializer *cs)\n> > >  {%- else %}\n> > > @@ -224,8 +224,8 @@\n> > >  \tstatic {{struct|name_full}}\n> > >  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t    std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t    std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t    std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t    std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  {%- if struct|needs_control_serializer %}\n> > >  \t\t    ControlSerializer *cs)\n> > >  {%- else %}\n> > > @@ -234,7 +234,7 @@\n> > >  \t{\n> > >  \t\t{{struct|name_full}} ret;\n> > >  \t\tstd::vector<uint8_t>::const_iterator m = dataBegin;\n> > > -\t\tstd::vector<FileDescriptor>::const_iterator n = fdsBegin;\n> > > +\t\tstd::vector<SharedFD>::const_iterator n = fdsBegin;\n> > >\n> > >  \t\tsize_t dataSize = std::distance(dataBegin, dataEnd);\n> > >  \t\t[[maybe_unused]] size_t fdsSize = std::distance(fdsBegin, fdsEnd);\n> > > @@ -255,7 +255,7 @@\n> > >  {%- macro deserializer_fd_simple(struct, namespace) %}\n> > >  \tstatic {{struct|name_full}}\n> > >  \tdeserialize(std::vector<uint8_t> &data,\n> > > -\t\t    [[maybe_unused]] std::vector<FileDescriptor> &fds,\n> > > +\t\t    [[maybe_unused]] std::vector<SharedFD> &fds,\n> > >  \t\t    ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(data.cbegin(), data.cend(), cs);\n> > > @@ -264,8 +264,8 @@\n> > >  \tstatic {{struct|name_full}}\n> > >  \tdeserialize(std::vector<uint8_t>::const_iterator dataBegin,\n> > >  \t\t    std::vector<uint8_t>::const_iterator dataEnd,\n> > > -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsBegin,\n> > > -\t\t    [[maybe_unused]] std::vector<FileDescriptor>::const_iterator fdsEnd,\n> > > +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsBegin,\n> > > +\t\t    [[maybe_unused]] std::vector<SharedFD>::const_iterator fdsEnd,\n> > >  \t\t    ControlSerializer *cs = nullptr)\n> > >  \t{\n> > >  \t\treturn IPADataSerializer<{{struct|name_full}}>::deserialize(dataBegin, dataEnd, cs);\n> > > diff --git a/utils/ipc/generators/mojom_libcamera_generator.py b/utils/ipc/generators/mojom_libcamera_generator.py\n> > > index c609f4e5c062..753bfc734e56 100644\n> > > --- a/utils/ipc/generators/mojom_libcamera_generator.py\n> > > +++ b/utils/ipc/generators/mojom_libcamera_generator.py\n> > > @@ -77,7 +77,7 @@ def GetDefaultValue(element):\n> > >      if mojom.IsEnumKind(element.kind):\n> > >          return f'static_cast<{element.kind.mojom_name}>(0)'\n> > >      if isinstance(element.kind, mojom.Struct) and \\\n> > > -       element.kind.mojom_name == 'FileDescriptor':\n> > > +       element.kind.mojom_name == 'SharedFD':\n> > >          return '-1'\n> > >      return ''\n> > >\n> > > @@ -140,7 +140,7 @@ def HasFd(element):\n> > >          types = GetAllTypes(element)\n> > >      else:\n> > >          types = GetAllTypes(element.kind)\n> > > -    return \"FileDescriptor\" in types or (attrs is not None and \"hasFd\" in attrs)\n> > > +    return \"SharedFD\" in types or (attrs is not None and \"hasFd\" in attrs)\n> > >\n> > >  def WithDefaultValues(element):\n> > >      return [x for x in element if HasDefaultValue(x)]\n> > > @@ -221,7 +221,7 @@ def IsEnum(element):\n> > >      return mojom.IsEnumKind(element.kind)\n> > >\n> > >  def IsFd(element):\n> > > -    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"FileDescriptor\"\n> > > +    return mojom.IsStructKind(element.kind) and element.kind.mojom_name == \"SharedFD\"\n> > >\n> > >  def IsMap(element):\n> > >      return mojom.IsMapKind(element.kind)\n>\n> --\n> Regards,\n>\n> Laurent Pinchart","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 68B86BDB13\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 29 Nov 2021 17:29:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 327E2605A6;\n\tMon, 29 Nov 2021 18:29:18 +0100 (CET)","from relay7-d.mail.gandi.net (relay7-d.mail.gandi.net\n\t[217.70.183.200])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D3D5760592\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 29 Nov 2021 18:29:16 +0100 (CET)","(Authenticated sender: jacopo@jmondi.org)\n\tby relay7-d.mail.gandi.net (Postfix) with ESMTPSA id 3382E2000C;\n\tMon, 29 Nov 2021 17:29:16 +0000 (UTC)"],"Date":"Mon, 29 Nov 2021 18:30:09 +0100","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<20211129173009.g6kggmxvheqokv5f@uno.localdomain>","References":"<20211128235752.10836-1-laurent.pinchart@ideasonboard.com>\n\t<20211128235752.10836-18-laurent.pinchart@ideasonboard.com>\n\t<20211129160055.p74ha6iidz7gxxqm@uno.localdomain>\n\t<YaUM35YDv12OEerK@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<YaUM35YDv12OEerK@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v3 17/17] libcamera: base: Rename\n\tFileDescriptor to SharedFD","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","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]