Pigweed
C/C++ API Reference
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Loading...
Searching...
No Matches
shared_ptr.h
1// Copyright 2025 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14#pragma once
15
16#include "pw_allocator/config.h"
17
18// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
19#if PW_ALLOCATOR_HAS_ATOMICS
20
21#include <cstddef>
22#include <cstdint>
23#include <utility>
24
25#include "pw_allocator/deallocator.h"
26#include "pw_allocator/internal/control_block.h"
27#include "pw_allocator/internal/managed_ptr.h"
28#include "pw_allocator/layout.h"
29
30namespace pw {
31
32// Forward declarations.
33template <typename T>
34class WeakPtr;
35
36enum class MultiBufProperty : uint8_t;
37
38template <MultiBufProperty...>
39class BasicMultiBuf;
40
56template <typename T>
57class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
58 private:
59 using Base = ::pw::allocator::internal::ManagedPtr<T>;
60 using ControlBlock = ::pw::allocator::internal::ControlBlock;
61
62 public:
63 using element_type = typename Base::element_type;
64 using weak_type = WeakPtr<T>;
65
67 ~SharedPtr() { reset(); }
68
73 constexpr SharedPtr() noexcept = default;
74
79 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
80
82 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
83 *this = other;
84 }
85
90 template <typename U>
91 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
92 *this = other;
93 }
94
100 template <typename U>
101 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
102 *this = std::move(other);
103 }
104
108 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
109 operator= <T>(other);
110 return *this;
111 }
112
119 template <typename U>
120 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
121
128 template <typename U>
129 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
130
135 SharedPtr& operator=(std::nullptr_t) noexcept;
136
140 size_t size() const {
141 static_assert(std::is_array_v<T>,
142 "size() cannot be called on non-array types");
143 return control_block_ == nullptr ? 0 : control_block_->size();
144 }
145
148 int32_t use_count() const {
149 return control_block_ == nullptr ? 0 : control_block_->num_shared();
150 }
151
157 void reset() noexcept;
158
160 void swap(SharedPtr& other) noexcept;
161
164 template <typename PtrType>
165 bool owner_before(const PtrType& other) const noexcept {
166 return control_block_ < other.control_block();
167 }
168
169 private:
170 using Layout = allocator::Layout;
171
172 static constexpr bool is_unbounded_array_v =
173 allocator::internal::is_unbounded_array_v<T>;
174
175 // Allow construction with to the implementation of `MakeShared`.
176 friend class Allocator;
177
178 // Allow SharedPtr<T> to access SharedPtr<U> and vice versa.
179 template <typename>
180 friend class SharedPtr;
181
182 // Allow WeakPtr<T> to promote to a SharedPtr<T>.
183 template <typename>
184 friend class WeakPtr;
185
186 // Allow MultiBufs to decompose SharedPtr<T>.
187 template <MultiBufProperty...>
188 friend class BasicMultiBuf;
189
200 template <typename... Args>
201 static SharedPtr Create(Allocator* allocator, Args&&... args);
202
214 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
215
220 SharedPtr(element_type* value, ControlBlock* control_block)
221 : Base(value), control_block_(control_block) {}
222
223 ControlBlock* control_block() const { return control_block_; }
224
226 template <typename U>
227 void CopyFrom(const SharedPtr<U>& other);
228
231 void Release();
232
234 template <typename U>
235 constexpr void CheckArrayTypes();
236
237 ControlBlock* control_block_ = nullptr;
238};
239
240// Template method implementations.
241
242template <typename T>
243template <typename... Args>
244SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
245 static_assert(!std::is_array_v<T>);
246 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>(), 1);
247 if (control_block == nullptr) {
248 return nullptr;
249 }
250 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
251 return SharedPtr<T>(t, control_block);
252}
253
254template <typename T>
255SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator,
256 size_t count,
257 size_t alignment) {
258 static_assert(allocator::internal::is_unbounded_array_v<T>);
259 Layout layout = Layout::Of<T>(count).Align(alignment);
260 auto* control_block = ControlBlock::Create(allocator, layout, count);
261 if (control_block == nullptr) {
262 return nullptr;
263 }
264 auto* t = new (control_block->data()) std::remove_extent_t<T>[count];
265 return SharedPtr<T>(t, control_block);
266}
267
268template <typename T>
269template <typename U>
270constexpr SharedPtr<T>& SharedPtr<T>::operator=(
271 const SharedPtr<U>& other) noexcept {
272 CheckArrayTypes<U>();
273 CopyFrom(other);
274 if (control_block_ != nullptr) {
275 control_block_->IncrementShared();
276 }
277 return *this;
278}
279
280template <typename T>
281template <typename U>
282SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
283 CheckArrayTypes<U>();
284 reset();
285 CopyFrom(other);
286 other.Release();
287 return *this;
288}
289
290template <typename T>
291SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
292 reset();
293 return *this;
294}
295
296template <typename T>
297void SharedPtr<T>::reset() noexcept {
298 if (*this == nullptr) {
299 return;
300 }
301 auto action = control_block_->DecrementShared();
302 if (action == ControlBlock::Action::kNone) {
303 // Other `SharedPtr`s associated with this control block remain.
304 Release();
305 return;
306 }
307
308 // This was the last `SharedPtr` associated with this control block.
309 Allocator* allocator = control_block_->allocator();
310 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
311 if constexpr (std::is_array_v<T>) {
312 Base::Destroy(control_block_->size());
313 } else {
314 Base::Destroy();
315 }
316 }
317
318 if (action == ControlBlock::Action::kExpire) {
319 // Keep the control block. Trying to promote any of the remaining
320 // `WeakPtr`s will fail.
321 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
322 } else {
323 // No `WeakPtr`s remain, and all of the memory can be freed.
324 Base::Deallocate(allocator, control_block_);
325 }
326
327 Release();
328}
329
330template <typename T>
331void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
332 Base::Swap(other);
333 std::swap(control_block_, other.control_block_);
334}
335
336template <typename T>
337template <typename U>
338void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
339 CheckArrayTypes<U>();
340 Base::CopyFrom(other);
341 control_block_ = other.control_block_;
342}
343
344template <typename T>
345void SharedPtr<T>::Release() {
346 Base::Release();
347 control_block_ = nullptr;
348}
349
350template <typename T>
351template <typename U>
352constexpr void SharedPtr<T>::CheckArrayTypes() {
353 if constexpr (std::is_array_v<T>) {
354 static_assert(std::is_array_v<U>,
355 "non-array type used with SharedPtr<T[]>");
356 } else {
357 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
358 }
359}
360
361} // namespace pw
362
363// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
364#endif // PW_ALLOCATOR_HAS_ATOMICS
Definition: allocator.h:34
Definition: multibuf_v2.h:193
Definition: shared_ptr.h:57
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:101
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
size_t size() const
Definition: shared_ptr.h:140
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:331
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:67
constexpr SharedPtr & operator=(const SharedPtr< U > &other) noexcept
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:82
int32_t use_count() const
Definition: shared_ptr.h:148
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:108
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:91
void reset() noexcept
Definition: shared_ptr.h:297
Definition: weak_ptr.h:35
Definition: layout.h:56
Provides basic helpers for reading and writing UTF-8 encoded strings.
Definition: alignment.h:27
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:22