frame_allocator

A frame allocator provides memory allocation for coroutine frames.

Requires: C++20

Synopsis

Defined in header <boost/capy/frame_allocator.hpp>

namespace boost::capy {

template<class A>
concept frame_allocator =
    std::copy_constructible<A> &&
    requires(A& a, void* p, std::size_t n) {
        { a.allocate(n) } -> std::same_as<void*>;
        { a.deallocate(p, n) };
    };

} // namespace boost::capy

Description

Frame allocators provide memory for coroutine frames—the compiler-generated structures holding local variables, parameters, and suspension state. A frame allocator must be a cheaply copyable handle to an underlying memory resource (e.g., a pointer to a pool).

The library copies the allocator into the first coroutine frame for lifetime safety. Subsequent frames in the call tree use the embedded allocator for both allocation and deallocation.

Default Frame Allocator

The library provides default_frame_allocator which passes through to ::operator new and ::operator delete:

struct default_frame_allocator
{
    void* allocate(std::size_t n)
    {
        return ::operator new(n);
    }

    void deallocate(void* p, std::size_t)
    {
        ::operator delete(p);
    }
};

Recycling Frame Allocator

By default, async_run uses a recycling frame allocator that caches deallocated frames for reuse. This eliminates most allocation overhead for typical coroutine patterns.

Valid Expressions

Given:

  • a — a value of type A

  • p — a value of type void*

  • n — a value of type std::size_t

Expression Return Type Description

a.allocate(n)

void*

Allocate n bytes for a coroutine frame

a.deallocate(p, n)

Deallocate memory previously allocated with allocate(n)

Example

#include <boost/capy/frame_allocator.hpp>
#include <boost/capy/async_run.hpp>

using boost::capy::frame_allocator;
using boost::capy::async_run;

class pool_frame_allocator
{
    memory_pool* pool_;

public:
    explicit pool_frame_allocator(memory_pool& pool)
        : pool_(&pool)
    {
    }

    void* allocate(std::size_t n)
    {
        return pool_->allocate(n);
    }

    void deallocate(void* p, std::size_t n)
    {
        pool_->deallocate(p, n);
    }
};

static_assert(frame_allocator<pool_frame_allocator>);

// Usage with async_run:
memory_pool pool;
pool_frame_allocator alloc{pool};

async_run(ex, alloc)(my_task());

See Also