Skip to content

Commit

Permalink
pw_allocator: Move code snippets from docs to examples
Browse files Browse the repository at this point in the history
What is better in than code samples in module documentation? Example
code that is actually compiled and tested as part of the build and the
included into the documentation!

This CL creates standalone examples as tests for all the code that
appears in the pw_allocator docs. This helps ensures the remain fresh
and accurate.

Bug: b/328076428
Change-Id: I853a21801c56093b8448e04f84d92e5245eab116
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/195190
Presubmit-Verified: CQ Bot Account <[email protected]>
Commit-Queue: Aaron Green <[email protected]>
Reviewed-by: Taylor Cramer <[email protected]>
  • Loading branch information
nopsledder authored and CQ Bot Account committed Mar 11, 2024
1 parent c66b857 commit 8160a4d
Show file tree
Hide file tree
Showing 20 changed files with 1,323 additions and 263 deletions.
17 changes: 16 additions & 1 deletion pw_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ pw_test_group("tests") {
":tracking_allocator_test",
":unique_ptr_test",
]
group_deps = [ "examples" ]
}

# Docs
Expand Down Expand Up @@ -467,7 +468,20 @@ pw_size_diff("allocator_utilities_size_report") {
}

pw_doc_group("docs") {
inputs = [ "doc_resources/pw_allocator_heap_visualizer_demo.png" ]
inputs = [
"doc_resources/pw_allocator_heap_visualizer_demo.png",
"examples/basic.cc",
"examples/block_allocator.cc",
"examples/custom_allocator_perf_test.cc",
"examples/custom_allocator_test_harness.h",
"examples/custom_allocator_test.cc",
"examples/custom_allocator.cc",
"examples/custom_allocator.h",
"examples/linker_sections.cc",
"examples/metrics.cc",
"examples/size_report.cc",
"examples/spin_lock.cc",
]
sources = [
"api.rst",
"code_size.rst",
Expand All @@ -478,5 +492,6 @@ pw_doc_group("docs") {
report_deps = [
":concrete_allocators_size_report",
":forwarding_allocators_size_report",
"examples:custom_allocator_size_report",
]
}
85 changes: 42 additions & 43 deletions pw_allocator/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ pw_allocator
- **Safe**: Can detect memory corruption, e.g overflows and use-after-free.
- **Measurable**: Pick what allocations you want to track and measure.

.. code-block:: cpp
.. literalinclude:: examples/basic.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-basic-allocate]
:end-before: [pw_allocator-examples-basic-allocate]

MyObject* Create(pw::allocator::Allocator& allocator) {
void* buf = allocator.Allocate(pw::allocator::Layout::Of<MyObject>());
return buf != nullptr ? new (buf) MyObject() : nullptr;
}
.. code-block:: cpp
// Any of these allocators can be passed to the routine above.
WithBuffer<LastFitBlockAllocator<uint32_t>, 0x1000> block_allocator;
Expand All @@ -39,62 +40,60 @@ increased shared memory, and reduced large reservations.
Use `dependency injection`_! Write your code to take
:ref:`module-pw_allocator-api-allocator` parameters, and you can quickly and
easily change where memory comes from or what additional features are provided
simply by changing what allocator is passed.

.. code-block:: cpp
// Set up allocators that allocate from SRAM or PSRAM memory.
PW_PLACE_IN_SECTION(".sram")
alignas(SmallFastObj) std::array<std::byte, 0x4000> sram_buffer;
pw::allocator::FirstFitBlockAllocator<uint16_t> sram_allocator(sram_buffer);
simply by changing what allocator is passed:

PW_PLACE_IN_SECTION(".psram")
std::array<std::byte, 0x40000> psram_buffer;
pw::allocator::WorstFitBlockAllocator<uint32_t>
psram_allocator(psram_buffer);
.. literalinclude:: examples/linker_sections.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-linker_sections-injection]
:end-before: [pw_allocator-examples-linker_sections-injection]

// Allocate objects from SRAM and PSRAM. Except for the name, the call sites
// are agnostic about the kind of memory used.
Layout small_fast_layout = pw::allocator::Layout::Of<SmallFastObj>();
void* small_fast_buf = sram_allocator.Allocate(small_fast_layout);
SmallFastObj* small_fast_obj = new (small_fast_buf) SmallFastObj();
Now you can easily allocate objects in the example above using SRAM, PSRAM, or
both:

Layout big_slow_layout = pw::allocator::Layout::Of<BigSlowObj>();
void* big_slow_buf = sram_allocator.Allocate(big_slow_layout);
BigSlowObj* big_slow_obj = new (big_slow_buf) BigSlowObj();
.. literalinclude:: examples/linker_sections.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-linker_sections-placement]
:end-before: [pw_allocator-examples-linker_sections-placement]

**Worried about forgetting to deallocate?**

Use a smart pointer!

.. code-block:: cpp
pw::allocator::UniquePtr<MyObject> ptr = allocator.MakeUnique<MyObject>();
.. literalinclude:: examples/basic.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-basic-make_unique]
:end-before: [pw_allocator-examples-basic-make_unique]

**Want to know how much memory has been allocated?**

Pick the metrics you're interested in and track them with a
:cpp:type:`pw::allocator::TrackingAllocator`:

.. code-block:: cpp
:ref:`module-pw_allocator-api-tracking_allocator`:

struct MyMetrics {
PW_ALLOCATOR_METRICS_ENABLE(allocated_bytes);
PW_ALLOCATOR_METRICS_ENABLE(peak_allocated_bytes);
};
.. literalinclude:: examples/metrics.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-metrics-custom_metrics1]
:end-before: [pw_allocator-examples-metrics-custom_metrics1]

pw::allocator::TrackingAllocator<MyMetrics> tracker(allocator);
.. literalinclude:: examples/metrics.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-metrics-custom_metrics2]
:end-before: [pw_allocator-examples-metrics-custom_metrics2]

**Need to share the allocator with another thread or an interrupt handler?**

Use a :cpp:type:`pw::allocator::SynchronizedAllocator` with the lock of your
choice:

.. code-block:: cpp
Use a :ref:`module-pw_allocator-api-synchronized_allocator` with the lock of
your choice:

pw::sync::InterruptSpinLock isl;
pw::allocator::SynchronizedAllocator<pw::sync::InterruptSpinLock>
synchronized(tracker, isl);
.. literalinclude:: examples/spin_lock.cc
:language: cpp
:linenos:
:start-after: [pw_allocator-examples-spin_lock]
:end-before: [pw_allocator-examples-spin_lock]

.. tip:: Check out the :ref:`module-pw_allocator-guides` for even more code
samples!
Expand Down
152 changes: 152 additions & 0 deletions pw_allocator/examples/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

load(
"//pw_build:pigweed.bzl",
"pw_cc_binary",
"pw_cc_test",
)

package(default_visibility = ["//visibility:public"])

licenses(["notice"])

# Libraries

cc_library(
name = "named_u32",
testonly = True,
hdrs = ["named_u32.h"],
includes = [".."],
deps = [
"//pw_bytes",
"//pw_string",
],
)

pw_cc_test(
name = "block_allocator",
srcs = ["block_allocator.cc"],
deps = [
":named_u32",
"//pw_allocator:block_allocator",
],
)

cc_library(
name = "custom_allocator",
testonly = True,
srcs = ["custom_allocator.cc"],
hdrs = ["custom_allocator.h"],
includes = [".."],
deps = [
"//pw_allocator:allocator",
"//pw_log",
],
)

cc_library(
name = "custom_allocator_test_harness",
testonly = True,
hdrs = ["custom_allocator_test_harness.h"],
includes = [".."],
deps = [
":custom_allocator",
"//pw_allocator:test_harness",
"//pw_allocator:testing",
],
)

# Examples

pw_cc_test(
name = "basic",
srcs = ["basic.cc"],
deps = [
":named_u32",
"//pw_allocator:allocator",
"//pw_allocator:testing",
],
)

pw_cc_test(
name = "custom_allocator_perf_test",
srcs = ["custom_allocator_perf_test.cc"],
deps = [
":custom_allocator_test_harness",
"//pw_perf_test",
"//pw_random",
],
)

pw_cc_test(
name = "custom_allocator_test",
srcs = ["custom_allocator_test.cc"],
deps = [
":custom_allocator",
":custom_allocator_test_harness",
":named_u32",
"//pw_allocator:fuzzing",
"//pw_allocator:testing",
"//pw_containers:vector",
"//pw_fuzzer:fuzztest",
],
)

pw_cc_test(
name = "linker_sections",
srcs = ["linker_sections.cc"],
deps = [
":named_u32",
"//pw_allocator:allocator",
"//pw_allocator:block_allocator",
],
)

pw_cc_test(
name = "metrics",
srcs = ["metrics.cc"],
deps = [
":named_u32",
"//pw_allocator:testing",
"//pw_allocator:tracking_allocator",
"//pw_tokenizer",
],
)

pw_cc_binary(
name = "size_report",
testonly = True,
srcs = ["size_report.cc"],
deps = [
":custom_allocator",
"//pw_allocator:block_allocator",
"//pw_allocator:size_reporter",
],
)

pw_cc_test(
name = "spin_lock",
srcs = ["spin_lock.cc"],
deps = [
":named_u32",
"//pw_allocator:synchronized_allocator",
"//pw_allocator:testing",
"//pw_assert",
"//pw_sync:interrupt_spin_lock",
"//pw_thread:test_thread_context",
"//pw_thread:thread",
"//pw_thread:thread_core",
],
)
Loading

0 comments on commit 8160a4d

Please sign in to comment.