Source
stdlib/memory/request.hpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
#pragma once5
/**6
* @file request.hpp7
* @brief `memory::Request<Lease>` — a promise for a lease; `.acquire(on_interrupt)` blocks.8
*9
* What `Owner::rread()` / `rwrite<…>()` return. Move-only; wraps a10
* `std::future<Lease>` (the owner fulfills the matching `std::promise` when it grants). `acquire()`11
* is the flow's single blocking redemption — though today's owner grants synchronously, so the wait12
* for the drain happens inside `rread()`/`rwrite<…>()` itself and the future a `Request` wraps is13
* already fulfilled by the time you hold one. The future/promise never surface in cheatah. No14
* `friend`: the interrupt handler is attached through the lease's public `on_interrupt()`.15
*/17
#include <functional>18
#include <future>19
#include <utility>21
#include "lease.hpp"23
namespace cheatah::memory {25
/**26
* @brief A promise for a lease — what `Owner::rread()`/`rwrite<…>()` return. Move-only; block for the27
* lease with `acquire()`. @tparam LeaseT the `Lease` type this request resolves to.28
*/29
template <class LeaseT>30
class Request {31
public:32
/// Wrap the owner-supplied future. @param fut the future the owner fulfills. @complexity O(1). @alloc none.33
/// @test Memory.EveryAccessorReturnsARequestNotABareLease34
explicit Request(std::future<LeaseT>&& fut) noexcept : fut_(std::move(fut)) {}35
/// Move-construct (a request is move-only). @complexity O(1). @alloc none.36
/// @test Memory.EveryAccessorReturnsARequestNotABareLease37
Request(Request&&) noexcept = default;38
/// Move-assign (a request is move-only). @return `*this`. @complexity O(1). @alloc none.39
/// @test Memory.EveryAccessorReturnsARequestNotABareLease40
Request& operator=(Request&&) noexcept = default;41
Request(const Request&) = delete;43
/**44
* Redeem the request: wait for the owner's grant, then return the lease. (Today's owner grants45
* synchronously inside `rread()`/`rwrite<…>()`, so the wrapped future is already fulfilled and46
* this returns at once.) @p on_interrupt is the requester's47
* interruption handler — wired to the granted lease so if the owner later needs the lease back the48
* handler fires. Omit it to rely purely on the lease's `valid()`/`expired()` polling. One-shot.49
* @param on_interrupt optional handler to run when the owner asks the lease to yield.50
* @return the granted lease.51
* @complexity O(1) once granted; blocks only until the owner fulfills the request (already done52
* by the time a `Request` exists today).53
* @alloc none, unless @p on_interrupt is supplied — then one callback holder (via `Lease::on_interrupt`).54
* @concurrency one-shot: a second `acquire()` on the same request throws `std::future_error`.55
* @p on_interrupt later fires on whichever thread polls the lease's `valid()` — never56
* asynchronously.57
* @test Memory.EveryAccessorReturnsARequestNotABareLease58
* @test Memory.InterruptCallbackFiresWhenTheOwnerNeedsTheLeaseBack59
*/60
LeaseT acquire(std::function<void()> on_interrupt = {}) {61
LeaseT lease = fut_.get(); // block until the owner fulfills the promise62
if (on_interrupt) lease.on_interrupt(std::move(on_interrupt));63
return lease; // moved (NRVO) into the caller's Lease64
}66
private:67
std::future<LeaseT> fut_; ///< owner-fulfilled; never exposed to cheatah.68
};70
} // namespace cheatah::memory