/proc/self/cwd/pw_allocator/control_block.cc
Line | Count | Source |
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 | | |
15 | | #include "pw_allocator/internal/control_block.h" |
16 | | |
17 | | #include "lib/stdcompat/bit.h" |
18 | | #include "pw_allocator/allocator.h" |
19 | | #include "pw_allocator/capability.h" |
20 | | #include "pw_allocator/deallocator.h" |
21 | | #include "pw_allocator/hardening.h" |
22 | | #include "pw_assert/check.h" |
23 | | #include "pw_bytes/alignment.h" |
24 | | |
25 | | namespace pw::allocator::internal { |
26 | | |
27 | 0 | const ControlBlockHandle& ControlBlockHandle::GetInstance_DO_NOT_USE() { |
28 | 0 | static const ControlBlockHandle kControlBlockHandle; |
29 | 0 | return kControlBlockHandle; |
30 | 0 | } |
31 | | |
32 | | ControlBlock* ControlBlock::Create(const ControlBlockHandle&, |
33 | | Allocator* allocator, |
34 | 0 | Layout layout) { |
35 | 0 | size_t size = layout.size(); |
36 | 0 | layout = layout.Extend(AlignUp(sizeof(ControlBlock), layout.alignment())); |
37 | 0 | void* ptr = allocator->Allocate(layout); |
38 | 0 | if (ptr == nullptr) { |
39 | 0 | return nullptr; |
40 | 0 | } |
41 | 0 | auto addr = cpp20::bit_cast<uintptr_t>(ptr); |
42 | 0 | addr = AlignUp(addr + sizeof(ControlBlock), layout.alignment()); |
43 | 0 | auto* data = cpp20::bit_cast<std::byte*>(addr); |
44 | 0 | return new (ptr) ControlBlock(allocator, data, size, true); |
45 | 0 | } |
46 | | |
47 | | ControlBlock* ControlBlock::Create(const ControlBlockHandle&, |
48 | | Deallocator* deallocator, |
49 | | void* data, |
50 | 0 | size_t size) { |
51 | 0 | if (!deallocator->HasCapability( |
52 | 0 | allocator::Capability::kCanAllocateArbitraryLayout)) { |
53 | 0 | return nullptr; |
54 | 0 | } |
55 | 0 | auto* allocator = static_cast<Allocator*>(deallocator); |
56 | 0 | void* ptr = allocator->Allocate(Layout::Of<ControlBlock>()); |
57 | 0 | if (ptr == nullptr) { |
58 | 0 | return nullptr; |
59 | 0 | } |
60 | 0 | return new (ptr) ControlBlock(allocator, data, size, false); |
61 | 0 | } |
62 | | |
63 | | ControlBlock::ControlBlock(Allocator* allocator, |
64 | | void* data, |
65 | | size_t size, |
66 | | bool coallocated) |
67 | 0 | : allocator_(allocator), |
68 | 0 | data_(data), |
69 | 0 | coallocated_and_size_((size << 1) | (coallocated ? 1 : 0)), |
70 | 0 | num_weak_and_shared_(Pack(1, 1)) { |
71 | 0 | PW_CHECK_UINT_EQ(size, GetSize()); |
72 | 0 | } |
73 | | |
74 | 0 | ControlBlock::~ControlBlock() { |
75 | 0 | if (!IsCoallocated()) { |
76 | 0 | allocator_->Deallocate(data_); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | 0 | int32_t ControlBlock::num_shared() const noexcept { |
81 | 0 | return UnpackShared(num_weak_and_shared_.load(std::memory_order_relaxed)); |
82 | 0 | } |
83 | | |
84 | 0 | bool ControlBlock::IncrementShared() { |
85 | 0 | uint32_t num = num_weak_and_shared_.load(std::memory_order_relaxed); |
86 | 0 | while (true) { |
87 | 0 | uint16_t num_weak = UnpackWeak(num); |
88 | 0 | uint16_t num_shared = UnpackShared(num); |
89 | | if constexpr (Hardening::kIncludesDebugChecks) { |
90 | | PW_CHECK_UINT_GE(num_weak, num_shared); |
91 | | } |
92 | 0 | if (num_shared == 0 || num_shared == 0xFFFF) { |
93 | 0 | return false; |
94 | 0 | } |
95 | 0 | uint32_t packed = Pack(num_weak + 1, num_shared + 1); |
96 | 0 | if (num_weak_and_shared_.compare_exchange_weak( |
97 | 0 | num, packed, std::memory_order_relaxed)) { |
98 | 0 | return true; |
99 | 0 | } |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | 0 | bool ControlBlock::IncrementWeak() { |
104 | 0 | uint32_t num = num_weak_and_shared_.load(std::memory_order_relaxed); |
105 | 0 | while (true) { |
106 | 0 | uint16_t num_weak = UnpackWeak(num); |
107 | 0 | uint16_t num_shared = UnpackShared(num); |
108 | | if constexpr (Hardening::kIncludesDebugChecks) { |
109 | | PW_CHECK_UINT_GE(num_weak, num_shared); |
110 | | } |
111 | 0 | if (num_weak == 0 || num_weak == 0xFFFF) { |
112 | 0 | return false; |
113 | 0 | } |
114 | 0 | uint32_t packed = Pack(num_weak + 1, num_shared); |
115 | 0 | if (num_weak_and_shared_.compare_exchange_weak( |
116 | 0 | num, packed, std::memory_order_relaxed)) { |
117 | 0 | return true; |
118 | 0 | } |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | 0 | ControlBlock::Action ControlBlock::DecrementShared() { |
123 | 0 | uint32_t prev = |
124 | 0 | num_weak_and_shared_.fetch_sub(Pack(1, 1), std::memory_order_acq_rel); |
125 | 0 | uint16_t prev_weak = UnpackWeak(prev); |
126 | 0 | uint16_t prev_shared = UnpackShared(prev); |
127 | | if constexpr (Hardening::kIncludesDebugChecks) { |
128 | | PW_CHECK_UINT_NE(prev_weak, 0); |
129 | | PW_CHECK_UINT_NE(prev_shared, 0); |
130 | | PW_CHECK_UINT_GE(prev_weak, prev_shared); |
131 | | } |
132 | 0 | if (prev_weak == 1 && prev_shared == 1) { |
133 | 0 | return ControlBlock::Action::kFree; |
134 | 0 | } |
135 | 0 | if (prev_shared == 1) { |
136 | 0 | return ControlBlock::Action::kExpire; |
137 | 0 | } |
138 | 0 | return ControlBlock::Action::kNone; |
139 | 0 | } |
140 | | |
141 | 0 | ControlBlock::Action ControlBlock::DecrementWeak() { |
142 | 0 | uint32_t prev = |
143 | 0 | num_weak_and_shared_.fetch_sub(Pack(1, 0), std::memory_order_acq_rel); |
144 | 0 | uint16_t prev_weak = UnpackWeak(prev); |
145 | | if constexpr (Hardening::kIncludesDebugChecks) { |
146 | | uint16_t prev_shared = UnpackShared(prev); |
147 | | PW_CHECK_UINT_NE(prev_weak, 0); |
148 | | PW_CHECK_UINT_GE(prev_weak, prev_shared); |
149 | | } |
150 | 0 | if (prev_weak == 1) { |
151 | 0 | return ControlBlock::Action::kFree; |
152 | 0 | } |
153 | 0 | return ControlBlock::Action::kNone; |
154 | 0 | } |
155 | | |
156 | | } // namespace pw::allocator::internal |