Coverage Report

Created: 2026-03-09 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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