libcamera: shared_mem_object: Reorganize the code and document the SharedMemObject class
The SharedMemObject class template contains a fair amount of inline code that does not depend on the template types T. To avoid duplicating it in every template specialization, split that code to a separate base SharedMem class. We don't define copy semantics for the classes (we don't need one at the moment) and we make them non-copyable since the default copy constructor would lead to use-after-unmap. Doxygen documentation by Dennis Bonke and Andrei Konovalov. Reviewed-by: Pavel Machek <pavel@ucw.cz> Reviewed-by: Milan Zamazal <mzamazal@redhat.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Co-developed-by: Dennis Bonke <admin@dennisbonke.com> Signed-off-by: Dennis Bonke <admin@dennisbonke.com> Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
This commit is contained in:
parent
ce3c8a5082
commit
9a2d7d3b6a
3 changed files with 290 additions and 50 deletions
|
@ -1,85 +1,98 @@
|
||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2023, Raspberry Pi Ltd
|
* Copyright (C) 2023 Raspberry Pi Ltd
|
||||||
|
* Copyright (C) 2024 Andrei Konovalov
|
||||||
|
* Copyright (C) 2024 Dennis Bonke
|
||||||
*
|
*
|
||||||
* shared_mem_object.h - Helper class for shared memory allocations
|
* shared_mem_object.h - Helpers for shared memory allocations
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <type_traits>
|
||||||
#include <unistd.h>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <libcamera/base/class.h>
|
#include <libcamera/base/class.h>
|
||||||
#include <libcamera/base/shared_fd.h>
|
#include <libcamera/base/shared_fd.h>
|
||||||
|
#include <libcamera/base/span.h>
|
||||||
|
|
||||||
namespace libcamera {
|
namespace libcamera {
|
||||||
|
|
||||||
template<class T>
|
class SharedMem
|
||||||
class SharedMemObject
|
{
|
||||||
|
public:
|
||||||
|
SharedMem();
|
||||||
|
|
||||||
|
SharedMem(const std::string &name, std::size_t size);
|
||||||
|
SharedMem(SharedMem &&rhs);
|
||||||
|
|
||||||
|
virtual ~SharedMem();
|
||||||
|
|
||||||
|
SharedMem &operator=(SharedMem &&rhs);
|
||||||
|
|
||||||
|
const SharedFD &fd() const
|
||||||
|
{
|
||||||
|
return fd_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<uint8_t> mem() const
|
||||||
|
{
|
||||||
|
return mem_;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return !mem_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LIBCAMERA_DISABLE_COPY(SharedMem)
|
||||||
|
|
||||||
|
SharedFD fd_;
|
||||||
|
|
||||||
|
Span<uint8_t> mem_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, typename = std::enable_if_t<std::is_standard_layout<T>::value>>
|
||||||
|
class SharedMemObject : public SharedMem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr std::size_t kSize = sizeof(T);
|
static constexpr std::size_t kSize = sizeof(T);
|
||||||
|
|
||||||
SharedMemObject()
|
SharedMemObject()
|
||||||
: obj_(nullptr)
|
: SharedMem(), obj_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
SharedMemObject(const std::string &name, Args &&...args)
|
SharedMemObject(const std::string &name, Args &&...args)
|
||||||
: name_(name), obj_(nullptr)
|
: SharedMem(name, kSize), obj_(nullptr)
|
||||||
{
|
{
|
||||||
void *mem;
|
if (mem().empty())
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
|
|
||||||
if (ret < 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fd_ = SharedFD(std::move(ret));
|
obj_ = new (mem().data()) T(std::forward<Args>(args)...);
|
||||||
if (!fd_.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
ret = ftruncate(fd_.get(), kSize);
|
|
||||||
if (ret < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
mem = mmap(nullptr, kSize, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
||||||
fd_.get(), 0);
|
|
||||||
if (mem == MAP_FAILED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
obj_ = new (mem) T(std::forward<Args>(args)...);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedMemObject(SharedMemObject<T> &&rhs)
|
SharedMemObject(SharedMemObject<T> &&rhs)
|
||||||
|
: SharedMem(std::move(rhs))
|
||||||
{
|
{
|
||||||
this->name_ = std::move(rhs.name_);
|
|
||||||
this->fd_ = std::move(rhs.fd_);
|
|
||||||
this->obj_ = rhs.obj_;
|
this->obj_ = rhs.obj_;
|
||||||
rhs.obj_ = nullptr;
|
rhs.obj_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
~SharedMemObject()
|
~SharedMemObject()
|
||||||
{
|
{
|
||||||
if (obj_) {
|
if (obj_)
|
||||||
obj_->~T();
|
obj_->~T();
|
||||||
munmap(obj_, kSize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make SharedMemObject non-copyable for now. */
|
|
||||||
LIBCAMERA_DISABLE_COPY(SharedMemObject)
|
|
||||||
|
|
||||||
SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
|
SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
|
||||||
{
|
{
|
||||||
this->name_ = std::move(rhs.name_);
|
SharedMem::operator=(std::move(rhs));
|
||||||
this->fd_ = std::move(rhs.fd_);
|
|
||||||
this->obj_ = rhs.obj_;
|
this->obj_ = rhs.obj_;
|
||||||
rhs.obj_ = nullptr;
|
rhs.obj_ = nullptr;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -105,19 +118,9 @@ public:
|
||||||
return *obj_;
|
return *obj_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SharedFD &fd() const
|
|
||||||
{
|
|
||||||
return fd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit operator bool() const
|
|
||||||
{
|
|
||||||
return !!obj_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string name_;
|
LIBCAMERA_DISABLE_COPY(SharedMemObject)
|
||||||
SharedFD fd_;
|
|
||||||
T *obj_;
|
T *obj_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ libcamera_sources = files([
|
||||||
'process.cpp',
|
'process.cpp',
|
||||||
'pub_key.cpp',
|
'pub_key.cpp',
|
||||||
'request.cpp',
|
'request.cpp',
|
||||||
|
'shared_mem_object.cpp',
|
||||||
'source_paths.cpp',
|
'source_paths.cpp',
|
||||||
'stream.cpp',
|
'stream.cpp',
|
||||||
'sysfs.cpp',
|
'sysfs.cpp',
|
||||||
|
|
236
src/libcamera/shared_mem_object.cpp
Normal file
236
src/libcamera/shared_mem_object.cpp
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Raspberry Pi Ltd
|
||||||
|
* Copyright (C) 2024 Andrei Konovalov
|
||||||
|
* Copyright (C) 2024 Dennis Bonke
|
||||||
|
* Copyright (C) 2024 Ideas on Board Oy
|
||||||
|
*
|
||||||
|
* shared_mem_object.cpp - Helpers for shared memory allocations
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libcamera/internal/shared_mem_object.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file shared_mem_object.cpp
|
||||||
|
* \brief Helpers for shared memory allocations
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace libcamera {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class SharedMem
|
||||||
|
* \brief Helper class to allocate and manage memory shareable between processes
|
||||||
|
*
|
||||||
|
* SharedMem manages memory suitable for sharing between processes. When an
|
||||||
|
* instance is constructed, it allocates a memory buffer of the requested size
|
||||||
|
* backed by an anonymous file, using the memfd API.
|
||||||
|
*
|
||||||
|
* The allocated memory is exposed by the mem() function. If memory allocation
|
||||||
|
* fails, the function returns an empty Span. This can be also checked using the
|
||||||
|
* bool() operator.
|
||||||
|
*
|
||||||
|
* The file descriptor for the backing file is exposed as a SharedFD by the fd()
|
||||||
|
* function. It can be shared with other processes across IPC boundaries, which
|
||||||
|
* can then map the memory with mmap().
|
||||||
|
*
|
||||||
|
* A single memfd is created for every SharedMem. If there is a need to allocate
|
||||||
|
* a large number of objects in shared memory, these objects should be grouped
|
||||||
|
* together and use the shared memory allocated by a single SharedMem object if
|
||||||
|
* possible. This will help to minimize the number of created memfd's.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SharedMem::SharedMem() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Construct a SharedMem with memory of the given \a size
|
||||||
|
* \param[in] name Name of the SharedMem
|
||||||
|
* \param[in] size Size of the shared memory to allocate and map
|
||||||
|
*
|
||||||
|
* The \a name is used for debugging purpose only. Multiple SharedMem instances
|
||||||
|
* can have the same name.
|
||||||
|
*/
|
||||||
|
SharedMem::SharedMem(const std::string &name, std::size_t size)
|
||||||
|
{
|
||||||
|
int fd = memfd_create(name.c_str(), MFD_CLOEXEC);
|
||||||
|
if (fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fd_ = SharedFD(std::move(fd));
|
||||||
|
if (!fd_.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ftruncate(fd_.get(), size) < 0) {
|
||||||
|
fd_ = SharedFD();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||||
|
fd_.get(), 0);
|
||||||
|
if (mem == MAP_FAILED) {
|
||||||
|
fd_ = SharedFD();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem_ = { static_cast<uint8_t *>(mem), size };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Move constructor for SharedMem
|
||||||
|
* \param[in] rhs The object to move
|
||||||
|
*/
|
||||||
|
SharedMem::SharedMem(SharedMem &&rhs)
|
||||||
|
{
|
||||||
|
this->fd_ = std::move(rhs.fd_);
|
||||||
|
this->mem_ = rhs.mem_;
|
||||||
|
rhs.mem_ = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destroy the SharedMem instance
|
||||||
|
*
|
||||||
|
* Destroying an instance invalidates the memory mapping exposed with mem().
|
||||||
|
* Other mappings of the backing file, created in this or other processes with
|
||||||
|
* mmap(), remain valid.
|
||||||
|
*
|
||||||
|
* Similarly, other references to the backing file descriptor created by copying
|
||||||
|
* the SharedFD returned by fd() remain valid. The underlying memory will be
|
||||||
|
* freed only when all file descriptors that reference the anonymous file get
|
||||||
|
* closed.
|
||||||
|
*/
|
||||||
|
SharedMem::~SharedMem()
|
||||||
|
{
|
||||||
|
if (!mem_.empty())
|
||||||
|
munmap(mem_.data(), mem_.size_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Move assignment operator for SharedMem
|
||||||
|
* \param[in] rhs The object to move
|
||||||
|
*/
|
||||||
|
SharedMem &SharedMem::operator=(SharedMem &&rhs)
|
||||||
|
{
|
||||||
|
this->fd_ = std::move(rhs.fd_);
|
||||||
|
this->mem_ = rhs.mem_;
|
||||||
|
rhs.mem_ = {};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn const SharedFD &SharedMem::fd() const
|
||||||
|
* \brief Retrieve the file descriptor for the underlying shared memory
|
||||||
|
* \return The file descriptor, or an invalid SharedFD if allocation failed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn Span<uint8_t> SharedMem::mem() const
|
||||||
|
* \brief Retrieve the underlying shared memory
|
||||||
|
* \return The memory buffer, or an empty Span if allocation failed
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMem::operator bool()
|
||||||
|
* \brief Check if the shared memory allocation succeeded
|
||||||
|
* \return True if allocation of the shared memory succeeded, false otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class SharedMemObject
|
||||||
|
* \brief Helper class to allocate an object in shareable memory
|
||||||
|
* \tparam The object type
|
||||||
|
*
|
||||||
|
* The SharedMemObject class is a specialization of the SharedMem class that
|
||||||
|
* wraps an object of type \a T and constructs it in shareable memory. It uses
|
||||||
|
* the same underlying memory allocation and sharing mechanism as the SharedMem
|
||||||
|
* class.
|
||||||
|
*
|
||||||
|
* The wrapped object is constructed at the same time as the SharedMemObject
|
||||||
|
* instance, by forwarding the arguments passed to the SharedMemObject
|
||||||
|
* constructor. The underlying memory allocation is sized to the object \a T
|
||||||
|
* size. The bool() operator should be used to check the allocation was
|
||||||
|
* successful. The object can be accessed using the dereference operators
|
||||||
|
* operator*() and operator->().
|
||||||
|
*
|
||||||
|
* While no restriction on the type \a T is enforced, not all types are suitable
|
||||||
|
* for sharing between multiple processes. Most notably, any object type that
|
||||||
|
* contains pointer or reference members will likely cause issues. Even if those
|
||||||
|
* members refer to other members of the same object, the shared memory will be
|
||||||
|
* mapped at different addresses in different processes, and the pointers will
|
||||||
|
* not be valid.
|
||||||
|
*
|
||||||
|
* A new anonymous file is created for every SharedMemObject instance. If there
|
||||||
|
* is a need to share a large number of small objects, these objects should be
|
||||||
|
* grouped into a single larger object to limit the number of file descriptors.
|
||||||
|
*
|
||||||
|
* To share the object with other processes, see the SharedMem documentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \var SharedMemObject::kSize
|
||||||
|
* \brief The size of the object stored in shared memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::SharedMemObject(const std::string &name, Args &&...args)
|
||||||
|
* \brief Construct a SharedMemObject
|
||||||
|
* \param[in] name Name of the SharedMemObject
|
||||||
|
* \param[in] args Arguments to pass to the constructor of the object T
|
||||||
|
*
|
||||||
|
* The \a name is used for debugging purpose only. Multiple SharedMem instances
|
||||||
|
* can have the same name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
|
||||||
|
* \brief Move constructor for SharedMemObject
|
||||||
|
* \param[in] rhs The object to move
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::~SharedMemObject()
|
||||||
|
* \brief Destroy the SharedMemObject instance
|
||||||
|
*
|
||||||
|
* Destroying a SharedMemObject calls the wrapped T object's destructor. While
|
||||||
|
* the underlying memory may not be freed immediately if other mappings have
|
||||||
|
* been created manually (see SharedMem::~SharedMem() for more information), the
|
||||||
|
* stored object may be modified. Depending on the ~T() destructor, accessing
|
||||||
|
* the object after destruction of the SharedMemObject causes undefined
|
||||||
|
* behaviour. It is the responsibility of the user of this class to synchronize
|
||||||
|
* with other users who have access to the shared object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
|
||||||
|
* \brief Move assignment operator for SharedMemObject
|
||||||
|
* \param[in] rhs The SharedMemObject object to take the data from
|
||||||
|
*
|
||||||
|
* Moving a SharedMemObject does not affect the stored object.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::operator->()
|
||||||
|
* \brief Dereference the stored object
|
||||||
|
* \return Pointer to the stored object
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn const T *SharedMemObject::operator->() const
|
||||||
|
* \copydoc SharedMemObject::operator->
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn SharedMemObject::operator*()
|
||||||
|
* \brief Dereference the stored object
|
||||||
|
* \return Reference to the stored object
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn const T &SharedMemObject::operator*() const
|
||||||
|
* \copydoc SharedMemObject::operator*
|
||||||
|
*/
|
||||||
|
|
||||||
|
} /* namespace libcamera */
|
Loading…
Add table
Add a link
Reference in a new issue