blob: d8d0ffb3c8510d3414175f80b1d105a036fe2b64 [file] [log] [blame] [view]
# LLCPP Memory Ownership
This document provides an overview of the tools available to manage memory when
using the LLCPP bindings.
## Pointers and memory ownership {#memory-ownership}
LLCPP objects use special smart pointers called `tracking_ptr` to keep track of memory ownership.
With `tracking_ptr`, LLCPP makes it possible for your code to easily set a value and forget
about ownership since `tracking_ptr` will take care of freeing memory when it goes out of scope.
These pointers have two states:
* Unowned (constructed from an `unowned_ptr_t`).
* Heap allocated and owned (constructed from a `std::unique_ptr`).
When the contents is owned, a `tracking_ptr` behaves like a `unique_ptr` and the pointer is
deleted on destruction. In the unowned state, `tracking_ptr` behaves like a raw pointer and
destruction is a no-op.
`tracking_ptr` is move-only and has an API closely matching `unique_ptr`.
### Types of object allocation
`tracking_ptr` makes it possible to create LLCPP objects with several allocation strategies.
The allocation strategies can be mixed and matched within the same code.
#### Heap allocation
To heap allocate objects, use the standard `std::make_unique`.
An example with an optional `uint32` field represented as a `tracking_ptr`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="heap-field" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
This applies to all union and table fields and data arrays within vectors and strings.
Vector and string data arrays must use the array specialization of `std::unique_ptr`,
which takes the element count as an argument.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="heap-vec" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To copy a collection to a `VectorView`, use `heap_copy_vec`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="heap-copy-vec" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To copy a string to a `StringView`, use `heap_copy_str`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="heap-copy-str" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
#### Allocators
FIDL provides an `Allocator` API that enables creating `tracking_ptr`s to LLCPP objects through a
number of allocation algorithms. Currently, `BufferThenHeapAllocator`, `UnsafeBufferAllocator`, and
`HeapAllocator` are available in fidl namespace.
The `BufferThenHeapAllocator` allocates from an in-band fixed-size buffer (can be used for stack
allocation), but falls back to heap allocation if the in-band buffer has been exhausted (to avoid
unnecessary unfortunate surprises). Be aware that excessive stack usage can cause its own problems,
so consider using a buffer size that comfortably fits on the stack, or consider putting the whole
BufferThenHeapAllocator on the heap if the buffer needs to be larger than fits on the stack, or
consider using HeapAllocator. Allocations must be assumed to be gone upon destruction of the
`BufferThenHeapAllocator` used to make them.
The `HeapAllocator` always allocates from the heap, and is unique among allocators (so far) in that
all of the `HeapAllocator` allocations can out-live the `HeapAllocator` instance used to make
them.
The `UnsafeBufferAllocator` is unsafe in the sense that it lacks heap failover, so risks creating
unfortunate data-dependent surprises unless the buffer size is absolutely guaranteed to be large
enough including the internal destructor-tracking overhead. If the internal buffer is exhausted,
make<>() will panic the entire process. Consider using `BufferThenHeapAllocator` instead. Do not
use `UnsafeBufferAllocator` without rigorously testing that the worst-case set of cumulative
allocations made through the allocator all fit without a panic, and consider how the rigor will be
maintained as code and FIDL tables are changed.
Example:
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="allocator-field" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
The arguments to `allocator.make` are identical to the arguments to `std::make_unique`.
This also applies to VectorViews.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="allocator-vec" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To copy a collection to a `VectorView` using an allocator, use `copy_vec`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="copy-vec" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To create a copy of a string using an allocator, use `copy_str`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="copy-str" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
#### Unowned pointers
In addition to the managed allocation strategies, it is also possible to directly
create pointers to memory unowned by FIDL. This is discouraged, as it is easy to
accidentally create use-after-free bugs. `unowned_ptr` exists to explicitly mark
pointers to FIDL-unowned memory.
The `unowned_ptr` helper is the recommended way to create `unowned_ptr_t`s,
which is more ergonomic than using the `unowned_ptr_t` constructor directly.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="unowned-ptr" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To create a `VectorView` from a collection using an unowned pointer to the
collection's data array, use `unowned_vec`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="unowned-vec" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
To create a `StringView` from unowned memory, use `unowned_str`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="unowned-str" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
A `StringView` can also be created directly from string literals without using
`unowned_ptr`.
```c++
{%includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/fidl/llcpp/unittests/main.cc" region_tag="stringview-assign" adjust_indentation="auto" exclude_regexp="^TEST|^}" %}
```
#### fidl::StringView
Defined in [lib/fidl/llcpp/string_view.h](/zircon/system/ulib/fidl/include/lib/fidl/llcpp/string_view.h)
Holds a reference to a variable-length string stored within the buffer. C++
wrapper of **fidl_string**. Does not own the memory of the contents.
`fidl::StringView` may be constructed by supplying the pointer and number of
UTF-8 bytes (excluding trailing `\0`) separately. Alternatively, one could pass
a C++ string literal, or any value which implements `[const] char* data()`
and `size()`. The string view would borrow the contents of the container.
It is memory layout compatible with **fidl_string**.
#### fidl::VectorView\<T\>
Defined in [lib/fidl/llcpp/vector_view.h](/zircon/system/ulib/fidl/include/lib/fidl/llcpp/vector_view.h)
Holds a reference to a variable-length vector of elements stored within the
buffer. C++ wrapper of **fidl_vector**. Does not own the memory of elements.
`fidl::VectorView` may be constructed by supplying the pointer and number of
elements separately. Alternatively, one could pass any value which supports
[`std::data`](https://en.cppreference.com/w/cpp/iterator/data), such as a
standard container, or an array. The vector view would borrow the contents of
the container.
It is memory layout compatible with **fidl_vector**.
#### fidl::Array\<T, N\>
Defined in [lib/fidl/llcpp/array.h](/zircon/system/ulib/fidl/include/lib/fidl/llcpp/array.h)
Owns a fixed-length array of elements.
Similar to `std::array<T, N>` but intended purely for in-place use.
It is memory layout compatible with FIDL arrays, and is standard-layout.
The destructor closes handles if applicable e.g. it is an array of handles.