summaryrefslogtreecommitdiff
path: root/src/libcamera/shared_mem_object.cpp
blob: b018fb3bc0a5fdcf5886df8ae4610744ca09d856 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
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 */