@@ -170,16 +170,23 @@ struct FutureValueType<future<T>> {
170170 * functions. If the value is visible, the retry loop will stop on the next
171171 * callback and/or before the next request or timer is issued.
172172 */
173- template <typename Functor, typename Request, typename RetryPolicyType>
173+ template <
174+ typename Functor, typename Request, typename RetryPolicyType,
175+ typename ReturnType = google::cloud::internal::invoke_result_t <
176+ Functor, google::cloud::CompletionQueue&,
177+ std::shared_ptr<grpc::ClientContext>, ImmutableOptions, Request const &>>
174178class AsyncRetryLoopImpl
175179 : public std::enable_shared_from_this<
176180 AsyncRetryLoopImpl<Functor, Request, RetryPolicyType>> {
177181 public:
178- AsyncRetryLoopImpl (std::unique_ptr<RetryPolicyType> retry_policy,
179- std::unique_ptr<BackoffPolicy> backoff_policy,
180- Idempotency idempotency, google::cloud::CompletionQueue cq,
181- Functor&& functor, ImmutableOptions options,
182- Request request, char const * location)
182+ AsyncRetryLoopImpl (
183+ std::unique_ptr<RetryPolicyType> retry_policy,
184+ std::unique_ptr<BackoffPolicy> backoff_policy, Idempotency idempotency,
185+ google::cloud::CompletionQueue cq, Functor&& functor,
186+ ImmutableOptions options, Request request, char const * location,
187+ std::function<
188+ bool (typename FutureValueType<ReturnType>::value_type const &)>
189+ attempt_predicate = {})
183190 : retry_policy_(std::move(retry_policy)),
184191 backoff_policy_ (std::move(backoff_policy)),
185192 idempotency_(idempotency),
@@ -188,11 +195,9 @@ class AsyncRetryLoopImpl
188195 functor_(std::forward<Functor>(functor)),
189196 request_(std::move(request)),
190197 location_(location),
191- call_context_(std::move(options)) {}
198+ call_context_(std::move(options)),
199+ attempt_predicate_(std::move(attempt_predicate)) {}
192200
193- using ReturnType = ::google::cloud::internal::invoke_result_t <
194- Functor, google::cloud::CompletionQueue&,
195- std::shared_ptr<grpc::ClientContext>, ImmutableOptions, Request const &>;
196201 using T = typename FutureValueType<ReturnType>::value_type;
197202
198203 future<T> Start () {
@@ -256,8 +261,16 @@ class AsyncRetryLoopImpl
256261 }
257262
258263 void OnAttempt (T result) {
259- // A successful attempt, set the value and finish the loop.
260- if (result.ok ()) return SetDone (std::move (result));
264+ if (attempt_predicate_) {
265+ // A successful attempt that satisfies the predicate, set the value and
266+ // finish the loop.
267+ if (result.ok () && attempt_predicate_ (result)) {
268+ return SetDone (std::move (result));
269+ }
270+ } else {
271+ // A successful attempt, set the value and finish the loop.
272+ if (result.ok ()) return SetDone (std::move (result));
273+ }
261274 // Some kind of failure, first verify that it is retryable.
262275 last_status_ = GetResultStatus (std::move (result));
263276 auto delay =
@@ -325,6 +338,8 @@ class AsyncRetryLoopImpl
325338 CallContext call_context_;
326339 Status last_status_;
327340 promise<T> result_;
341+ std::function<bool (typename FutureValueType<ReturnType>::value_type const &)>
342+ attempt_predicate_;
328343
329344 // Only the following variables require synchronization, as they coordinate
330345 // the work between the retry loop (which would be lock-free) and the cancel
@@ -339,17 +354,23 @@ class AsyncRetryLoopImpl
339354/* *
340355 * Create the right AsyncRetryLoopImpl object and start the retry loop on it.
341356 */
342- template <typename Functor, typename Request, typename RetryPolicyType,
343- std::enable_if_t <google::cloud::internal::is_invocable<
344- Functor, google::cloud::CompletionQueue&,
345- std::shared_ptr<grpc::ClientContext>,
346- ImmutableOptions, Request const &>::value,
347- int > = 0 >
348- auto AsyncRetryLoop (std::unique_ptr<RetryPolicyType> retry_policy,
349- std::unique_ptr<BackoffPolicy> backoff_policy,
350- Idempotency idempotency, google::cloud::CompletionQueue cq,
351- Functor&& functor, ImmutableOptions options,
352- Request request, char const * location)
357+ template <
358+ typename Functor, typename Request, typename RetryPolicyType,
359+ std::enable_if_t <google::cloud::internal::is_invocable<
360+ Functor, google::cloud::CompletionQueue&,
361+ std::shared_ptr<grpc::ClientContext>, ImmutableOptions,
362+ Request const &>::value,
363+ int > = 0 ,
364+ typename ReturnType = google::cloud::internal::invoke_result_t <
365+ Functor, google::cloud::CompletionQueue&,
366+ std::shared_ptr<grpc::ClientContext>, ImmutableOptions, Request const &>>
367+ auto AsyncRetryLoop (
368+ std::unique_ptr<RetryPolicyType> retry_policy,
369+ std::unique_ptr<BackoffPolicy> backoff_policy, Idempotency idempotency,
370+ google::cloud::CompletionQueue cq, Functor&& functor,
371+ ImmutableOptions options, Request request, char const * location,
372+ std::function<bool (typename FutureValueType<ReturnType>::value_type const &)>
373+ attempt_predicate = {})
353374 -> google::cloud::internal::invoke_result_t <
354375 Functor, google::cloud::CompletionQueue&,
355376 std::shared_ptr<grpc::ClientContext>, ImmutableOptions,
@@ -358,7 +379,7 @@ auto AsyncRetryLoop(std::unique_ptr<RetryPolicyType> retry_policy,
358379 std::make_shared<AsyncRetryLoopImpl<Functor, Request, RetryPolicyType>>(
359380 std::move (retry_policy), std::move (backoff_policy), idempotency,
360381 std::move (cq), std::forward<Functor>(functor), options,
361- std::move (request), location);
382+ std::move (request), location, std::move (attempt_predicate) );
362383 return loop->Start ();
363384}
364385
0 commit comments