C/C++ API Reference
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
33
34// Forward declarations.
35template <typename T>
36class WeakPtr;
37
38enum class MultiBufProperty : uint8_t;
39
40template <MultiBufProperty...>
41class BasicMultiBuf;
42
58template <typename T>
59class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
60 private:
61 using Base = ::pw::allocator::internal::ManagedPtr<T>;
62 using ControlBlock = ::pw::allocator::internal::ControlBlock;
63
64 public:
65 using element_type = typename Base::element_type;
66 using weak_type = WeakPtr<T>;
67
69 ~SharedPtr() { reset(); }
70
75 constexpr SharedPtr() noexcept = default;
76
81 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
82
84 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
85 *this = other;
86 }
87
92 template <typename U>
93 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
94 *this = other;
95 }
96
102 template <typename U>
103 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
104 *this = std::move(other);
105 }
106
110 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
111 operator= <T>(other);
112 return *this;
113 }
114
121 template <typename U>
122 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
123
130 template <typename U>
131 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
132
137 SharedPtr& operator=(std::nullptr_t) noexcept;
138
142 size_t size() const {
143 static_assert(std::is_array_v<T>,
144 "size() cannot be called on non-array types");
145 return control_block_ == nullptr ? 0 : control_block_->size();
146 }
147
150 int32_t use_count() const {
151 return control_block_ == nullptr ? 0 : control_block_->num_shared();
152 }
153
159 void reset() noexcept;
160
162 void swap(SharedPtr& other) noexcept;
163
166 template <typename PtrType>
167 bool owner_before(const PtrType& other) const noexcept {
168 return control_block_ < other.control_block();
169 }
170
171 private:
172 using Layout = allocator::Layout;
173
174 static constexpr bool is_unbounded_array_v =
175 allocator::internal::is_unbounded_array_v<T>;
176
177 // Allow construction with to the implementation of `MakeShared`.
178 friend class Allocator;
179
180 // Allow SharedPtr<T> to access SharedPtr<U> and vice versa.
181 template <typename>
182 friend class SharedPtr;
183
184 // Allow WeakPtr<T> to promote to a SharedPtr<T>.
185 template <typename>
186 friend class WeakPtr;
187
188 // Allow MultiBufs to decompose SharedPtr<T>.
189 template <MultiBufProperty...>
190 friend class BasicMultiBuf;
191
202 template <typename... Args>
203 static SharedPtr Create(Allocator* allocator, Args&&... args);
204
216 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
217
222 SharedPtr(element_type* value, ControlBlock* control_block)
223 : Base(value), control_block_(control_block) {}
224
225 ControlBlock* control_block() const { return control_block_; }
226
228 template <typename U>
229 void CopyFrom(const SharedPtr<U>& other);
230
233 void Release();
234
236 template <typename U>
237 constexpr void CheckArrayTypes();
238
239 ControlBlock* control_block_ = nullptr;
240};
241
243
244// Template method implementations.
245
246template <typename T>
247template <typename... Args>
248SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
249 static_assert(!std::is_array_v<T>);
250 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>(), 1);
251 if (control_block == nullptr) {
252 return nullptr;
253 }
254 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
255 return SharedPtr<T>(t, control_block);
256}
257
258template <typename T>
259SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator,
260 size_t count,
261 size_t alignment) {
262 static_assert(allocator::internal::is_unbounded_array_v<T>);
263 Layout layout = Layout::Of<T>(count).Align(alignment);
264 auto* control_block = ControlBlock::Create(allocator, layout, count);
265 if (control_block == nullptr) {
266 return nullptr;
267 }
268 auto* t = new (control_block->data()) std::remove_extent_t<T>[count];
269 return SharedPtr<T>(t, control_block);
270}
271
272template <typename T>
273template <typename U>
274constexpr SharedPtr<T>& SharedPtr<T>::operator=(
275 const SharedPtr<U>& other) noexcept {
276 CheckArrayTypes<U>();
277 CopyFrom(other);
278 if (control_block_ != nullptr) {
279 control_block_->IncrementShared();
280 }
281 return *this;
282}
283
284template <typename T>
285template <typename U>
286SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
287 CheckArrayTypes<U>();
288 reset();
289 CopyFrom(other);
290 other.Release();
291 return *this;
292}
293
294template <typename T>
295SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
296 reset();
297 return *this;
298}
299
300template <typename T>
301void SharedPtr<T>::reset() noexcept {
302 if (*this == nullptr) {
303 return;
304 }
305 auto action = control_block_->DecrementShared();
306 if (action == ControlBlock::Action::kNone) {
307 // Other `SharedPtr`s associated with this control block remain.
308 Release();
309 return;
310 }
311
312 // This was the last `SharedPtr` associated with this control block.
313 Allocator* allocator = control_block_->allocator();
314 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
315 if constexpr (std::is_array_v<T>) {
316 Base::Destroy(control_block_->size());
317 } else {
318 Base::Destroy();
319 }
320 }
321
322 if (action == ControlBlock::Action::kExpire) {
323 // Keep the control block. Trying to promote any of the remaining
324 // `WeakPtr`s will fail.
325 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
326 } else {
327 // No `WeakPtr`s remain, and all of the memory can be freed.
328 Base::Deallocate(allocator, control_block_);
329 }
330
331 Release();
332}
333
334template <typename T>
335void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
336 Base::Swap(other);
337 std::swap(control_block_, other.control_block_);
338}
339
340template <typename T>
341template <typename U>
342void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
343 CheckArrayTypes<U>();
344 Base::CopyFrom(other);
345 control_block_ = other.control_block_;
346}
347
348template <typename T>
349void SharedPtr<T>::Release() {
350 Base::Release();
351 control_block_ = nullptr;
352}
353
354template <typename T>
355template <typename U>
356constexpr void SharedPtr<T>::CheckArrayTypes() {
357 if constexpr (std::is_array_v<T>) {
358 static_assert(std::is_array_v<U>,
359 "non-array type used with SharedPtr<T[]>");
360 } else {
361 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
362 }
363}
364
365} // namespace pw
366
367// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
368#endif // PW_ALLOCATOR_HAS_ATOMICS
Definition: allocator.h:36
Definition: multibuf_v2.h:192
Definition: shared_ptr.h:59
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:103
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
size_t size() const
Definition: shared_ptr.h:142
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:335
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:69
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:84
int32_t use_count() const
Definition: shared_ptr.h:150
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:110
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:93
void reset() noexcept
Definition: shared_ptr.h:301
Definition: weak_ptr.h:37
Definition: layout.h:58
MultiBufProperty
Basic properties of a MultiBuf.
Definition: properties.h:24
The Pigweed namespace.
Definition: alignment.h:27