cheatah
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 once
5/**
6 * @file request.hpp
7 * @brief `memory::Request<Lease>` — a promise for a lease; `.acquire(on_interrupt)` blocks.
8 *
9 * What `Owner::rread()` / `rwrite<…>()` return. Move-only; wraps a
10 * `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 wait
12 * for the drain happens inside `rread()`/`rwrite<…>()` itself and the future a `Request` wraps is
13 * already fulfilled by the time you hold one. The future/promise never surface in cheatah. No
14 * `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"
23namespace cheatah::memory {
25/**
26 * @brief A promise for a lease — what `Owner::rread()`/`rwrite<…>()` return. Move-only; block for the
27 * lease with `acquire()`. @tparam LeaseT the `Lease` type this request resolves to.
28 */
29template <class LeaseT>
30class Request {
31public:
32 /// Wrap the owner-supplied future. @param fut the future the owner fulfills. @complexity O(1). @alloc none.
33 /// @test Memory.EveryAccessorReturnsARequestNotABareLease
34 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.EveryAccessorReturnsARequestNotABareLease
37 Request(Request&&) noexcept = default;
38 /// Move-assign (a request is move-only). @return `*this`. @complexity O(1). @alloc none.
39 /// @test Memory.EveryAccessorReturnsARequestNotABareLease
40 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 grants
45 * synchronously inside `rread()`/`rwrite<…>()`, so the wrapped future is already fulfilled and
46 * this returns at once.) @p on_interrupt is the requester's
47 * interruption handler — wired to the granted lease so if the owner later needs the lease back the
48 * 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 done
52 * 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()` — never
56 * asynchronously.
57 * @test Memory.EveryAccessorReturnsARequestNotABareLease
58 * @test Memory.InterruptCallbackFiresWhenTheOwnerNeedsTheLeaseBack
59 */
60 LeaseT acquire(std::function<void()> on_interrupt = {}) {
61 LeaseT lease = fut_.get(); // block until the owner fulfills the promise
62 if (on_interrupt) lease.on_interrupt(std::move(on_interrupt));
63 return lease; // moved (NRVO) into the caller's Lease
64 }
66private:
67 std::future<LeaseT> fut_; ///< owner-fulfilled; never exposed to cheatah.
68};
70} // namespace cheatah::memory