10#ifndef NROS_RCLCPP_FUTURE_HPP
11#define NROS_RCLCPP_FUTURE_HPP
17#include "rclcpp/size_bound.hpp"
21#include "rclcpp/logging.hpp"
41template <
typename A>
class Client;
97 uint8_t*
buf =
nullptr;
135 void (*
forget)(
void* ctx,
const uint8_t goal[16]) =
nullptr;
183 from->
next =
nullptr;
197 return (
head !=
nullptr &&
head->next ==
nullptr) ?
head :
nullptr;
200 if (l->seq == seq)
return l;
213 if (!l->by_goal)
continue;
215 for (
int i = 0; i < 16; ++i) {
216 if (l->goal_id[i] != goal[i]) {
233 l->storage = storage;
244 while (
head !=
nullptr) {
262 if (l->by_goal || l->seq != seq)
continue;
275 while (l !=
nullptr) {
289 const size_t n = len < owner->
cap ? len : owner->
cap;
290 if (owner->
buf != data) {
291 for (
size_t i = 0; i < n; ++i)
292 owner->
buf[i] = data[i];
296 (void)
unlink(owner,
false);
337template <typename T, size_t Cap = ::rclcpp::rx_buffer_capacity<T>::value>
class Future {
346 if (!live())
return false;
347 if (link_.ready)
return true;
348 if (take_seq_fn_ !=
nullptr)
return poll_keyed();
349 if (pump_fn_ !=
nullptr)
return poll_pumped();
350 if (!take_fn_)
return false;
352 nros_cpp_ret_t ret = take_fn_(link_.storage, cached_buf_,
sizeof(cached_buf_), &len);
353 if (ret == 0 && len > 0) {
355 link_.len = len <
sizeof(cached_buf_) ? len :
sizeof(cached_buf_);
369 if (T::ffi_deserialize(cached_buf_, link_.len, &out) != 0) {
386 Result wait(
void* executor_handle, uint32_t timeout_ms, T& out, uint32_t poll_ms = 10) {
388 if (poll_ms == 0) poll_ms = 1;
395 const uint64_t budget_ns =
static_cast<uint64_t
>(timeout_ms) * 1000000ULL;
408 if (now_ns - start_ns >= budget_ns)
break;
426 bool valid()
const {
return live(); }
428#if defined(NROS_CPP_HAS_SHARED_PTR)
447 ::std::shared_ptr<T> get() {
448 if (!live() || (!link_.ready && !
is_ready())) {
449 NROS_RCLCPP_SAY_REFUSED(NROS_RCLCPP_REFUSE_FUTURE_GET_NOT_READY_RUNTIME);
450 return ::std::shared_ptr<T>();
452 ::std::shared_ptr<T> out = ::std::make_shared<T>();
453 if (!
try_take(*out).
ok()) return ::std::shared_ptr<T>();
459 template <
typename U =
void>
void get() {
460 static_assert(::rclcpp::detail::refuse<U>::value, NROS_RCLCPP_REFUSE_FUTURE_GET);
464#if defined(NROS_CPP_HAS_STD_FUTURE)
480 template <typename Rep, typename Period>
481 ::std::future_status wait_for(::std::chrono::duration<Rep, Period> timeout) {
482 if (!live()) return ::std::future_status::timeout;
483 if (link_.ready || is_ready()) return ::std::future_status::ready;
484 const int64_t ms = ::rclcpp::detail::chrono_wait_budget_ms(timeout);
485 if (ms <= 0 || executor_ ==
nullptr) {
487 return is_ready() ? ::std::future_status::ready : ::std::future_status::timeout;
490 const uint64_t budget_ns =
static_cast<uint64_t
>(ms) * 1000000ULL;
493 if (elapsed_ns >= budget_ns)
break;
494 const uint64_t left_ms = (budget_ns - elapsed_ns + 999999ULL) / 1000000ULL;
495 const int32_t slice = left_ms < 10ULL ? static_cast<int32_t>(left_ms) : 10;
497 if (is_ready()) return ::std::future_status::ready;
499 return is_ready() ? ::std::future_status::ready : ::std::future_status::timeout;
510 : take_fn_(other.take_fn_), take_seq_fn_(other.take_seq_fn_), pump_fn_(other.pump_fn_),
511 executor_(other.executor_), slot_(other.slot_) {
516 if (
this != &other) {
518 take_fn_ = other.take_fn_;
519 take_seq_fn_ = other.take_seq_fn_;
520 pump_fn_ = other.pump_fn_;
521 executor_ = other.executor_;
532 : take_fn_(nullptr), take_seq_fn_(nullptr), pump_fn_(nullptr), executor_(nullptr),
534 link_.buf = cached_buf_;
535 link_.cap =
sizeof(cached_buf_);
542 template <
typename S>
friend class ::rclcpp::PollClient;
543 template <
typename A>
friend class ::rclcpp_action::Client;
545 using TryRecvFn =
nros_cpp_ret_t (*)(
void*, uint8_t*, size_t,
size_t*);
553 Future(
void* storage, TryRecvFn fn,
int slot,
void* executor =
nullptr)
554 : take_fn_(fn), take_seq_fn_(nullptr), pump_fn_(nullptr), executor_(executor), slot_(slot) {
555 link_.buf = cached_buf_;
556 link_.cap =
sizeof(cached_buf_);
557 link_.storage = storage;
564 : take_fn_(nullptr), take_seq_fn_(fn), pump_fn_(nullptr), executor_(executor), slot_(0) {
565 link_.buf = cached_buf_;
566 link_.cap =
sizeof(cached_buf_);
567 link_.storage = storage;
570 if (list !=
nullptr) list->
link(&link_);
579 : take_fn_(nullptr), take_seq_fn_(nullptr), pump_fn_(pump), executor_(executor), slot_(0) {
580 link_.buf = cached_buf_;
581 link_.cap =
sizeof(cached_buf_);
582 link_.storage = storage;
583 link_.by_goal =
true;
584 for (
int i = 0; i < 16; ++i)
585 link_.goal_id[i] = goal[i];
587 if (list !=
nullptr) list->
link(&link_);
590 bool live()
const {
return slot_ >= 0 && !link_.orphaned; }
595 ::rclcpp::detail::PendingList* list = link_.list;
596 if (list ==
nullptr)
return false;
601 take_seq_fn_(link_.storage, cached_buf_,
sizeof(cached_buf_), &len, &seq);
603 ::rclcpp::detail::PendingLink* owner = list->
resolve(seq);
604 if (owner !=
nullptr) list->
deliver(owner, cached_buf_, len);
605 if (link_.ready)
return true;
613 if (link_.list ==
nullptr || link_.storage ==
nullptr)
return link_.ready;
614 (void)pump_fn_(link_.storage);
620 void detach(
bool abandoned) {
621 if (link_.list !=
nullptr && !link_.ready) (void)link_.list->
unlink(&link_, abandoned);
622 link_.list =
nullptr;
626 void adopt(
Future& other) {
627 link_.buf = cached_buf_;
628 link_.cap =
sizeof(cached_buf_);
629 link_.storage = other.link_.storage;
630 link_.seq = other.link_.seq;
631 link_.by_goal = other.link_.by_goal;
632 for (
int i = 0; i < 16; ++i)
633 link_.goal_id[i] = other.link_.goal_id[i];
634 link_.ready = other.link_.ready;
635 link_.orphaned = other.link_.orphaned;
636 link_.len = other.link_.len;
637 link_.list = other.link_.list;
638 link_.next =
nullptr;
640 for (
size_t i = 0; i < link_.len; ++i)
641 cached_buf_[i] = other.cached_buf_[i];
643 if (link_.list !=
nullptr && !link_.ready) link_.list->relink(&other.link_, &link_);
644 other.link_.list =
nullptr;
645 other.link_.ready =
false;
646 other.link_.next =
nullptr;
657 ::rclcpp::detail::PendingLink link_;
658 uint8_t cached_buf_[Cap];
Definition future.hpp:337
Future & operator=(Future &&other) noexcept
Definition future.hpp:515
Result wait(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition future.hpp:386
bool valid() const
Definition future.hpp:426
Future(Future &&other) noexcept
Definition future.hpp:509
Future()
Default constructor – creates an empty/consumed future.
Definition future.hpp:531
~Future()
Definition future.hpp:528
bool is_consumed() const
Check if the future has been consumed or cancelled.
Definition future.hpp:504
void cancel()
Definition future.hpp:416
Result try_take(T &out)
Definition future.hpp:365
void get()
Definition future.hpp:459
bool is_ready()
Definition future.hpp:345
bool ok() const
Returns true if the operation succeeded.
Definition result.hpp:227
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:239
Definition subscription.hpp:197
Definition client.hpp:256
int nros_cpp_ret_t
Definition future.hpp:25
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition rclcpp.hpp:418
nros_cpp_ret_t(*)(void *, uint8_t *, size_t, size_t *, int64_t *) TakeSeqFn
Definition future.hpp:67
nros_cpp_ret_t(*)(void *) PumpFn
Definition future.hpp:75
constexpr int MAX_STALE_DRAIN_PER_POLL
Definition future.hpp:63
constexpr int64_t SEQUENCE_ID_UNKNOWN
Definition future.hpp:59
@ Error
Generic failure not covered by a more specific code.
Definition result.hpp:118
@ Timeout
Operation deadline elapsed before completion.
Definition result.hpp:120
@ TryAgain
Transient — no data ready yet (non-blocking take). Retry later.
Definition result.hpp:148
ResultOf< void > Result
Definition result.hpp:247
**auto handle
Definition client.hpp:127
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
PendingLink * next
Definition future.hpp:83
size_t len
Definition future.hpp:99
uint8_t goal_id[16]
Definition future.hpp:96
bool by_goal
Definition future.hpp:95
int64_t seq
Definition future.hpp:90
bool orphaned
The client went away while the request was pending: never answerable.
Definition future.hpp:102
bool ready
Definition future.hpp:100
uint8_t * buf
the future's own receive buffer
Definition future.hpp:97
void * storage
Definition future.hpp:89
PendingList * list
Definition future.hpp:86
size_t cap
Definition future.hpp:98
Definition future.hpp:126
PendingLink * resolve(int64_t seq)
Definition future.hpp:191
PendingLink * resolve_goal(const uint8_t goal[16])
Definition future.hpp:211
void orphan_all()
Definition future.hpp:273
bool remove_seq(int64_t seq)
Definition future.hpp:259
void move_to(PendingList &to, void *storage)
Definition future.hpp:228
void * forget_ctx
Definition future.hpp:136
void link(PendingLink *l)
Definition future.hpp:151
void(* forget)(void *ctx, const uint8_t goal[16])
Definition future.hpp:135
size_t count() const
Definition future.hpp:138
PendingLink * head
Definition future.hpp:127
uint8_t orphans
Definition future.hpp:128
void deliver(PendingLink *owner, const uint8_t *data, size_t len)
Hand a reply to owner, which is answered and leaves the list.
Definition future.hpp:288
size_t prune()
Definition future.hpp:242
bool unlink(PendingLink *l, bool abandoned)
Definition future.hpp:158
void relink(PendingLink *from, PendingLink *to)
A moved future: the list must point at its new address.
Definition future.hpp:178
bool can_send() const
Definition future.hpp:145