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#ifndef PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
17#define PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
18
19#include "pw_allocator/config.h"
20
21// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
22#if PW_ALLOCATOR_HAS_ATOMICS
23
24#include <cstddef>
25#include <cstdint>
26#include <utility>
27
28#include "pw_allocator/allocator.h"
29#include "pw_allocator/deallocator.h"
30#include "pw_allocator/internal/control_block.h"
31#include "pw_allocator/internal/managed_ptr.h"
32#include "pw_allocator/layout.h"
33#include "pw_allocator/unique_ptr.h"
34#include "pw_multibuf/v2/properties.h"
35
36namespace pw {
37
39
40// Forward declarations.
41template <typename T>
42class WeakPtr;
43
44namespace multibuf::v2 {
45
46template <Property...>
47class BasicMultiBuf;
48
49} // namespace multibuf::v2
50
66template <typename T>
67class SharedPtr final : public ::pw::allocator::internal::ManagedPtr<T> {
68 private:
69 using Base = ::pw::allocator::internal::ManagedPtr<T>;
70 using ControlBlock = ::pw::allocator::internal::ControlBlock;
71
72 public:
73 using element_type = typename Base::element_type;
74 using weak_type = WeakPtr<T>;
75
77 ~SharedPtr() { reset(); }
78
83 constexpr SharedPtr() noexcept = default;
84
89 constexpr SharedPtr(std::nullptr_t) noexcept : SharedPtr() {}
90
92 constexpr SharedPtr(const SharedPtr& other) noexcept : SharedPtr() {
93 *this = other;
94 }
95
100 template <typename U,
101 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
102 constexpr SharedPtr(const SharedPtr<U>& other) noexcept : SharedPtr() {
103 *this = other;
104 }
105
111 template <typename U,
112 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
113 SharedPtr(SharedPtr<U>&& other) noexcept : SharedPtr() {
114 *this = std::move(other);
115 }
116
126 SharedPtr(UniquePtr<T>& owned) noexcept;
127
131 constexpr SharedPtr& operator=(const SharedPtr& other) noexcept {
132 operator= <T>(other);
133 return *this;
134 }
135
142 template <typename U,
143 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
144 constexpr SharedPtr& operator=(const SharedPtr<U>& other) noexcept;
145
152 template <typename U,
153 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
154 SharedPtr& operator=(SharedPtr<U>&& other) noexcept;
155
160 SharedPtr& operator=(std::nullptr_t) noexcept;
161
173 template <typename U,
174 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
175 constexpr explicit operator const SharedPtr<U>&() const {
176 return static_cast<const SharedPtr<U>&>(
177 static_cast<const allocator::internal::BaseManagedPtr&>(*this));
178 }
179
180 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
181 std::nullptr_t) {
182 return lhs.Equals(nullptr);
183 }
184 [[nodiscard]] friend constexpr bool operator==(std::nullptr_t,
185 const SharedPtr& rhs) {
186 return rhs.Equals(nullptr);
187 }
188 [[nodiscard]] friend constexpr bool operator==(const SharedPtr& lhs,
189 const SharedPtr& rhs) {
190 return lhs.Equals(rhs) && lhs.control_block_ == rhs.control_block_;
191 }
192
193 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
194 std::nullptr_t) {
195 return !lhs.Equals(nullptr);
196 }
197 [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t,
198 const SharedPtr& rhs) {
199 return !rhs.Equals(nullptr);
200 }
201 [[nodiscard]] friend constexpr bool operator!=(const SharedPtr& lhs,
202 const SharedPtr& rhs) {
203 return !(lhs == rhs);
204 }
205
209 size_t size() const {
210 static_assert(std::is_array_v<T>,
211 "size() cannot be called on non-array types");
212 return control_block_ == nullptr ? 0 : control_block_->size();
213 }
214
217 int32_t use_count() const {
218 return control_block_ == nullptr ? 0 : control_block_->num_shared();
219 }
220
226 void reset() noexcept;
227
229 void swap(SharedPtr& other) noexcept;
230
233 template <typename PtrType>
234 bool owner_before(const PtrType& other) const noexcept {
235 return control_block_ < other.control_block();
236 }
237
238 private:
239 using Layout = allocator::Layout;
240
241 static constexpr bool is_unbounded_array_v =
242 allocator::internal::is_unbounded_array_v<T>;
243
244 // Allow construction with to the implementation of `MakeShared`.
245 friend class Allocator;
246
247 // Allow SharedPtr<T> to access SharedPtr<U> and vice versa.
248 template <typename>
249 friend class SharedPtr;
250
251 // Allow WeakPtr<T> to promote to a SharedPtr<T>.
252 template <typename>
253 friend class WeakPtr;
254
255 // Allow MultiBufs to decompose SharedPtr<T>.
257
258 template <multibuf::v2::Property...>
259 friend class multibuf::v2::BasicMultiBuf;
260
271 template <typename... Args>
272 static SharedPtr Create(Allocator* allocator, Args&&... args);
273
285 static SharedPtr Create(Allocator* allocator, size_t count, size_t alignment);
286
291 SharedPtr(element_type* value, ControlBlock* control_block)
292 : Base(value), control_block_(control_block) {}
293
294 ControlBlock* control_block() const { return control_block_; }
295
297 template <typename U,
298 typename = std::enable_if_t<std::is_assignable_v<T*&, U*>>>
299 void CopyFrom(const SharedPtr<U>& other);
300
303 void Release();
304
306 template <typename U>
307 constexpr void CheckArrayTypes();
308
309 ControlBlock* control_block_ = nullptr;
310};
311
313
314// Template method implementations.
315
316template <typename T>
318 if constexpr (std::is_array_v<T>) {
319 control_block_ =
320 ControlBlock::Create(owned.deallocator(), owned.get(), owned.size());
321 } else {
322 control_block_ = ControlBlock::Create(owned.deallocator(), owned.get(), 1);
323 }
324 if (control_block_ != nullptr) {
325 Base::CopyFrom(owned);
326 owned.Release();
327 }
328}
329
330template <typename T>
331template <typename... Args>
332SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator, Args&&... args) {
333 static_assert(!std::is_array_v<T>);
334 auto* control_block = ControlBlock::Create(allocator, Layout::Of<T>(), 1);
335 if (control_block == nullptr) {
336 return nullptr;
337 }
338 auto* t = new (control_block->data()) T(std::forward<Args>(args)...);
339 return SharedPtr<T>(t, control_block);
340}
341
342template <typename T>
343SharedPtr<T> SharedPtr<T>::Create(Allocator* allocator,
344 size_t count,
345 size_t alignment) {
346 static_assert(allocator::internal::is_unbounded_array_v<T>);
347 Layout layout = Layout::Of<T>(count).Align(alignment);
348 auto* control_block = ControlBlock::Create(allocator, layout, count);
349 if (control_block == nullptr) {
350 return nullptr;
351 }
352 auto* t = new (control_block->data()) std::remove_extent_t<T>[count];
353 return SharedPtr<T>(t, control_block);
354}
355
356template <typename T>
357template <typename U, typename>
358constexpr SharedPtr<T>& SharedPtr<T>::operator=(
359 const SharedPtr<U>& other) noexcept {
360 CheckArrayTypes<U>();
361 CopyFrom(other);
362 if (control_block_ != nullptr) {
363 control_block_->IncrementShared();
364 }
365 return *this;
366}
367
368template <typename T>
369template <typename U, typename>
370SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<U>&& other) noexcept {
371 CheckArrayTypes<U>();
372 reset();
373 CopyFrom(other);
374 other.Release();
375 return *this;
376}
377
378template <typename T>
379SharedPtr<T>& SharedPtr<T>::operator=(std::nullptr_t) noexcept {
380 reset();
381 return *this;
382}
383
384template <typename T>
385void SharedPtr<T>::reset() noexcept {
386 if (*this == nullptr) {
387 return;
388 }
389 auto action = control_block_->DecrementShared();
390 if (action == ControlBlock::Action::kNone) {
391 // Other `SharedPtr`s associated with this control block remain.
392 Release();
393 return;
394 }
395
396 // This was the last `SharedPtr` associated with this control block.
397 Allocator* allocator = control_block_->allocator();
398 if (!Base::HasCapability(allocator, allocator::kSkipsDestroy)) {
399 if constexpr (std::is_array_v<T>) {
400 Base::Destroy(control_block_->size());
401 } else {
402 Base::Destroy();
403 }
404 }
405
406 if (action == ControlBlock::Action::kExpire) {
407 // Keep the control block. Trying to promote any of the remaining
408 // `WeakPtr`s will fail.
409 Base::Resize(allocator, control_block_, sizeof(ControlBlock));
410 } else {
411 // No `WeakPtr`s remain, and all of the memory can be freed.
412 std::destroy_at(control_block_);
413 Base::Deallocate(allocator, control_block_);
414 }
415
416 Release();
417}
418
419template <typename T>
420void SharedPtr<T>::swap(SharedPtr<T>& other) noexcept {
421 Base::Swap(other);
422 std::swap(control_block_, other.control_block_);
423}
424
425template <typename T>
426template <typename U, typename>
427void SharedPtr<T>::CopyFrom(const SharedPtr<U>& other) {
428 CheckArrayTypes<U>();
429 Base::CopyFrom(other);
430 control_block_ = other.control_block_;
431}
432
433template <typename T>
434void SharedPtr<T>::Release() {
435 Base::Release();
436 control_block_ = nullptr;
437}
438
439template <typename T>
440template <typename U>
441constexpr void SharedPtr<T>::CheckArrayTypes() {
442 if constexpr (std::is_array_v<T>) {
443 static_assert(std::is_array_v<U>,
444 "non-array type used with SharedPtr<T[]>");
445 } else {
446 static_assert(!std::is_array_v<U>, "array type used with SharedPtr<T>");
447 }
448}
449
450} // namespace pw
451
452// TODO(b/402489948): Remove when portable atomics are provided by `pw_atomic`.
453#endif // PW_ALLOCATOR_HAS_ATOMICS
454
455#endif // PW_ALLOCATOR_PUBLIC_PW_ALLOCATOR_SHARED_PTR_H_
Definition: allocator.h:45
Definition: shared_ptr.h:67
size_t size() const
Definition: shared_ptr.h:209
void swap(SharedPtr &other) noexcept
Swaps the managed pointer and deallocator of this and another object.
Definition: shared_ptr.h:420
constexpr SharedPtr & operator=(const SharedPtr< U > &other) noexcept
SharedPtr & operator=(SharedPtr< U > &&other) noexcept
SharedPtr(SharedPtr< U > &&other) noexcept
Definition: shared_ptr.h:113
~SharedPtr()
Releases any currently-held value.
Definition: shared_ptr.h:77
constexpr SharedPtr() noexcept=default
constexpr SharedPtr(const SharedPtr &other) noexcept
Copy-constructs a SharedPtr<T> from a SharedPtr<T>.
Definition: shared_ptr.h:92
int32_t use_count() const
Definition: shared_ptr.h:217
constexpr SharedPtr(const SharedPtr< U > &other) noexcept
Definition: shared_ptr.h:102
constexpr SharedPtr & operator=(const SharedPtr &other) noexcept
Definition: shared_ptr.h:131
void reset() noexcept
Definition: shared_ptr.h:385
Definition: unique_ptr.h:43
Definition: weak_ptr.h:37
Definition: layout.h:58
Definition: multibuf.h:184
Property
Basic properties of a MultiBuf.
Definition: properties.h:24
The Pigweed namespace.
Definition: alignment.h:27