nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
future.hpp
Go to the documentation of this file.
1// nros-cpp: Future<T> -- single-shot deferred result
2// Freestanding C++ -- no exceptions, no STL required
3
10#ifndef NROS_RCLCPP_FUTURE_HPP
11#define NROS_RCLCPP_FUTURE_HPP
12
13#include <cstdint>
14#include <cstddef>
15
16#include "rclcpp/result.hpp"
17#include "rclcpp/size_bound.hpp" // rclcpp::rx_buffer_capacity<T> — the receive-buffer size
18
19// FFI declarations
20extern "C" {
21typedef int nros_cpp_ret_t;
22nros_cpp_ret_t nros_cpp_spin_once(void* handle, int32_t timeout_ms);
25uint64_t nros_cpp_time_ns(void);
26}
27
28// phase-428 (RFC-0089): the entity types that reach into this one's private
29// constructor are DEFINED in the upstream namespace now, so the friend
30// declarations below name them there. A qualified friend cannot introduce a
31// name, hence these declarations.
32namespace rclcpp {
33template <typename M> class Subscription;
34} // namespace rclcpp
35
36namespace rclcpp_action {
37template <typename A> class Client;
38} // namespace rclcpp_action
39
40namespace rclcpp {
41
42// phase-456 W9 — `send_request` and the `Future` it hands back moved off
43// `rclcpp::Client<S>` onto `rclcpp::PollClient<S>`, the half that owns the
44// `RmwServiceClient` this future takes from. The friend list FOLLOWS the method:
45// the poll client is declared here, and `rclcpp::Client<S>` left both this
46// declaration and the friend list below, because a friendship nothing uses is a
47// claim about the code that is not true.
48template <typename S> class PollClient;
49
71 public:
73 bool is_ready() {
74 if (slot_ < 0 || !take_fn_) return false;
75 if (ready_) return true;
77 size_t len = 0;
78 nros_cpp_ret_t ret = take_fn_(client_storage_, buf, sizeof(buf), &len);
79 if (ret == 0 && len > 0) {
80 ready_ = true;
81 cached_len_ = len < sizeof(cached_buf_) ? len : sizeof(cached_buf_);
82 for (size_t i = 0; i < cached_len_; ++i)
83 cached_buf_[i] = buf[i];
84 return true;
85 }
86 return false;
87 }
88
94 if (slot_ < 0) return Result(ErrorCode::Error);
95 if (!ready_ && !is_ready()) return Result(ErrorCode::Error);
96 slot_ = -1; // consume
97 if (T::ffi_deserialize(cached_buf_, cached_len_, &out) != 0) {
99 }
100 return Result::success();
101 }
102
114 Result wait(void* executor_handle, uint32_t timeout_ms, T& out, uint32_t poll_ms = 10) {
115 if (slot_ < 0) return Result(ErrorCode::Error);
116 if (poll_ms == 0) poll_ms = 1;
117 // Phase 89.2: budget by wall-clock. Accumulating `step` per iteration
118 // breaks when the underlying `zpico_spin_once` returns early on a
119 // signaled condvar (keep-alives, discovery gossip) — the 500-step
120 // default-timeout loop can then collapse into milliseconds and
121 // return ErrorCode::Timeout before the reply has a chance to land.
123 const uint64_t budget_ns = static_cast<uint64_t>(timeout_ms) * 1000000ULL;
124 while (true) {
125 nros_cpp_ret_t ret = nros_cpp_spin_once(executor_handle, static_cast<int32_t>(poll_ms));
126 // Transient conditions: keep polling. Anything else propagates.
127 // - Ok (0): nothing to dispatch this round.
128 // - Timeout (-2): spin_once returned after its timeout — normal.
129 // - TryAgain (-6): transport hint to retry.
130 if (ret != 0 && ret != static_cast<nros_cpp_ret_t>(ErrorCode::Timeout) &&
131 ret != static_cast<nros_cpp_ret_t>(ErrorCode::TryAgain)) {
132 return Result(ret);
133 }
134 if (is_ready()) return try_take(out);
136 if (now_ns - start_ns >= budget_ns) break;
137 }
139 }
140
142 void cancel() { slot_ = -1; }
143
145 bool is_consumed() const { return slot_ < 0; }
146
147 // Move semantics (non-copyable, single-shot)
149 : client_storage_(other.client_storage_), take_fn_(other.take_fn_), slot_(other.slot_),
150 ready_(other.ready_), cached_len_(other.cached_len_) {
151 for (size_t i = 0; i < cached_len_; ++i)
152 cached_buf_[i] = other.cached_buf_[i];
153 other.slot_ = -1;
154 other.ready_ = false;
155 }
156
158 if (this != &other) {
159 client_storage_ = other.client_storage_;
160 take_fn_ = other.take_fn_;
161 slot_ = other.slot_;
162 ready_ = other.ready_;
163 cached_len_ = other.cached_len_;
164 for (size_t i = 0; i < cached_len_; ++i)
165 cached_buf_[i] = other.cached_buf_[i];
166 other.slot_ = -1;
167 other.ready_ = false;
168 }
169 return *this;
170 }
171
173
176 : client_storage_(nullptr), take_fn_(nullptr), slot_(-1), ready_(false), cached_len_(0) {}
177
178 private:
179 Future(const Future&) = delete;
180 Future& operator=(const Future&) = delete;
181
182 template <typename S> friend class ::rclcpp::PollClient;
183 template <typename A> friend class ::rclcpp_action::Client;
184
185 using TryRecvFn = nros_cpp_ret_t (*)(void*, uint8_t*, size_t, size_t*);
186
187 Future(void* storage, TryRecvFn fn, int slot)
188 : client_storage_(storage), take_fn_(fn), slot_(slot), ready_(false), cached_len_(0) {}
189
190 void* client_storage_;
191 TryRecvFn take_fn_;
192 int slot_;
193 bool ready_;
194 size_t cached_len_;
195 uint8_t cached_buf_[Cap];
196};
197
198} // namespace rclcpp
199#endif // NROS_RCLCPP_FUTURE_HPP
Definition client.hpp:129
Definition future.hpp:70
Future & operator=(Future &&other) noexcept
Definition future.hpp:157
Result wait(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition future.hpp:114
Future(Future &&other) noexcept
Definition future.hpp:148
Future()
Default constructor – creates an empty/consumed future.
Definition future.hpp:175
friend class ::rclcpp_action::Client
Definition future.hpp:183
~Future()
Definition future.hpp:172
bool is_consumed() const
Check if the future has been consumed or cancelled.
Definition future.hpp:145
void cancel()
Cancel the pending operation (idempotent).
Definition future.hpp:142
Result try_take(T &out)
Definition future.hpp:93
bool is_ready()
Check if the result has arrived (non-blocking).
Definition future.hpp:73
Definition result.hpp:216
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:238
int nros_cpp_ret_t
Definition future.hpp:21
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition node.hpp:199
Definition rclcpp.hpp:75
ResultOf< void > Result
Definition result.hpp:246
@ Error
Generic failure not covered by a more specific code.
@ Timeout
Operation deadline elapsed before completion.
@ TryAgain
Transient — no data ready yet (non-blocking take). Retry later.
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.