Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Fix generated C++ bounded-wait RPCs so each promise yields exactly one
future, reused for both the timeout wait and the result read. Void RPCs now
also fulfill their promise after decoding a successful response; previously
non-Void calls threw `future_already_retrieved` and Void calls always timed
out even when the backend had completed.

- Add executable product acceptance contracts. `raco rivet acceptance
--json` validates `rivet-acceptance.rktd`, rejects missing kind/platform
coverage, launches each current-platform suite as an executable plus argv
Expand Down
10 changes: 6 additions & 4 deletions platform/linux/host/GeneratedBackend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class API {
std::optional<std::string> greet_within(
std::string name, std::chrono::milliseconds rivet_timeout) {
auto rivet_done = std::make_shared<std::promise<std::string>>();
auto rivet_future = rivet_done->get_future();
auto rivet_id = backend_.request_async(
"greet", rivet::linux_runtime::Value::List{rivet::Value(std::move(name))},
[rivet_done](rivet::linux_runtime::CallResult call) mutable {
Expand All @@ -85,17 +86,18 @@ class API {
rivet_done->set_value(*result.value);
}
});
if (rivet_done->get_future().wait_for(rivet_timeout) !=
if (rivet_future.wait_for(rivet_timeout) !=
std::future_status::ready) {
backend_.cancel(rivet_id);
return std::nullopt;
}
return rivet_done->get_future().get();
return rivet_future.get();
}

std::optional<std::int64_t> increment_within(
std::int64_t value, std::chrono::milliseconds rivet_timeout) {
auto rivet_done = std::make_shared<std::promise<std::int64_t>>();
auto rivet_future = rivet_done->get_future();
auto rivet_id = backend_.request_async(
"increment", rivet::linux_runtime::Value::List{rivet::Value(value)},
[rivet_done](rivet::linux_runtime::CallResult call) mutable {
Expand All @@ -106,12 +108,12 @@ class API {
rivet_done->set_value(*result.value);
}
});
if (rivet_done->get_future().wait_for(rivet_timeout) !=
if (rivet_future.wait_for(rivet_timeout) !=
std::future_status::ready) {
backend_.cancel(rivet_id);
return std::nullopt;
}
return rivet_done->get_future().get();
return rivet_future.get();
}

// State accessors: completion callbacks run on the backend reader thread
Expand Down
10 changes: 6 additions & 4 deletions platform/windows/host/GeneratedBackend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class API {
std::optional<std::string> greet_within(
std::string name, std::chrono::milliseconds rivet_timeout) {
auto rivet_done = std::make_shared<std::promise<std::string>>();
auto rivet_future = rivet_done->get_future();
auto rivet_id = backend_.request_async(
"greet", rivet::windows::Value::List{rivet::Value(std::move(name))},
[rivet_done](rivet::windows::CallResult call) mutable {
Expand All @@ -85,17 +86,18 @@ class API {
rivet_done->set_value(*result.value);
}
});
if (rivet_done->get_future().wait_for(rivet_timeout) !=
if (rivet_future.wait_for(rivet_timeout) !=
std::future_status::ready) {
backend_.cancel(rivet_id);
return std::nullopt;
}
return rivet_done->get_future().get();
return rivet_future.get();
}

std::optional<std::int64_t> increment_within(
std::int64_t value, std::chrono::milliseconds rivet_timeout) {
auto rivet_done = std::make_shared<std::promise<std::int64_t>>();
auto rivet_future = rivet_done->get_future();
auto rivet_id = backend_.request_async(
"increment", rivet::windows::Value::List{rivet::Value(value)},
[rivet_done](rivet::windows::CallResult call) mutable {
Expand All @@ -106,12 +108,12 @@ class API {
rivet_done->set_value(*result.value);
}
});
if (rivet_done->get_future().wait_for(rivet_timeout) !=
if (rivet_future.wait_for(rivet_timeout) !=
std::future_status::ready) {
backend_.cancel(rivet_id);
return std::nullopt;
}
return rivet_done->get_future().get();
return rivet_future.get();
}

// State accessors: completion callbacks run on the backend reader thread
Expand Down
9 changes: 5 additions & 4 deletions rivet-cli/codegen/cpp.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@
(define (cpp-promise-handler result-type backend-namespace)
(define settle
(if (eq? result-type 'Void)
(format "detail::decode_~a(*rivet_result.value);" (type-key result-type))
(format "detail::decode_~a(*rivet_result.value); rivet_done->set_value();"
(type-key result-type))
(format "rivet_done->set_value(detail::decode_~a(*rivet_result.value));"
(type-key result-type))))
(format
Expand Down Expand Up @@ -186,16 +187,16 @@
(define timed-out-return (if void? "false" "std::nullopt"))
(define final-get
(if void?
"rivet_done->get_future().get(); return true;"
"return rivet_done->get_future().get();"))
"rivet_future.get(); return true;"
"return rivet_future.get();"))
;; A zero-argument RPC has no parameter list; the timeout must not follow
;; a stray comma.
(define within-params
(if (string=? params "")
"std::chrono::milliseconds rivet_timeout"
(format "~a, std::chrono::milliseconds rivet_timeout" params)))
(format
" ~a ~a_within(~a) { auto rivet_done = std::make_shared<std::promise<~a>>(); auto rivet_id = backend_.request_async(~a, rivet::Value::List{~a}, ~a); if (rivet_done->get_future().wait_for(rivet_timeout) != std::future_status::ready) { backend_.cancel(rivet_id); return ~a; } ~a }\n"
" ~a ~a_within(~a) { auto rivet_done = std::make_shared<std::promise<~a>>(); auto rivet_future = rivet_done->get_future(); auto rivet_id = backend_.request_async(~a, rivet::Value::List{~a}, ~a); if (rivet_future.wait_for(rivet_timeout) != std::future_status::ready) { backend_.cancel(rivet_id); return ~a; } ~a }\n"
return-type
(cpp-id (schema-rpc-name info))
within-params
Expand Down
12 changes: 12 additions & 0 deletions rivet/tests/codegen.rkt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ RKT
(check-true (string-contains? cpp "bool log_line_within(std::string message, std::chrono::milliseconds rivet_timeout)"))
(check-true (string-contains? cpp "backend_.cancel(rivet_id); return false;"))
(check-true (string-contains? cpp "rivet_done->set_value(detail::decode_Int64(*rivet_result.value));"))
(check-true
(string-contains? cpp
"auto rivet_future = rivet_done->get_future();"))
(check-true
(string-contains? cpp
"rivet_future.wait_for(rivet_timeout)"))
(check-true (string-contains? cpp "return rivet_future.get();"))
(check-false
(string-contains? cpp "rivet_done->get_future().wait_for"))
(check-true
(string-contains? cpp
"detail::decode_Void(*rivet_result.value); rivet_done->set_value();"))
;; A Void async handler must not assign into Result<void>, which has no
;; value member — the decode only validates arrival.
(check-false (string-contains? cpp "result.value = detail::decode_void"))
Expand Down
Loading