[promises] Log errors in TrySeq (#35319)

When an error occurs in `TrySeq`, log the error.
Also, in the trace statements, capture the file/line that the sequence was constructed at, and log that with the traces.

Closes #35319

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/35319 from ctiller:trace-seq bc0d47395f
PiperOrigin-RevId: 591999228
pull/35334/head
Craig Tiller 1 year ago committed by Copybara-Service
parent b589b107c8
commit 6d35000c17
  1. 1
      BUILD
  2. 8
      src/core/BUILD
  3. 4
      src/core/lib/gprpp/debug_location.h
  4. 567
      src/core/lib/promise/detail/seq_state.h
  5. 69
      src/core/lib/promise/seq.h
  6. 81
      src/core/lib/promise/try_seq.h
  7. 25
      tools/codegen/core/gen_seq.py

@ -2511,6 +2511,7 @@ grpc_cc_library(
language = "c++",
public_hdrs = ["//src/core:lib/gprpp/debug_location.h"],
visibility = ["@grpc:debug_location"],
deps = ["gpr_platform"],
)
grpc_cc_library(

@ -754,7 +754,10 @@ grpc_cc_library(
grpc_cc_library(
name = "seq_state",
external_deps = ["absl/base:core_headers"],
external_deps = [
"absl/base:core_headers",
"absl/strings",
],
language = "c++",
public_hdrs = [
"lib/promise/detail/seq_state.h",
@ -765,6 +768,7 @@ grpc_cc_library(
"promise_factory",
"promise_like",
"promise_trace",
"//:debug_location",
"//:gpr",
],
)
@ -780,6 +784,7 @@ grpc_cc_library(
"poll",
"promise_like",
"seq_state",
"//:debug_location",
"//:gpr_platform",
],
)
@ -801,6 +806,7 @@ grpc_cc_library(
"promise_like",
"promise_status",
"seq_state",
"//:debug_location",
"//:gpr_platform",
],
)

@ -19,6 +19,10 @@
#ifndef GRPC_SRC_CORE_LIB_GPRPP_DEBUG_LOCATION_H
#define GRPC_SRC_CORE_LIB_GPRPP_DEBUG_LOCATION_H
#include <grpc/support/port_platform.h>
#include <utility>
#if defined(__has_builtin)
#if __has_builtin(__builtin_FILE)
#define GRPC_DEFAULT_FILE __builtin_FILE()

File diff suppressed because it is too large Load Diff

@ -21,6 +21,7 @@
#include <utility>
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/promise/detail/basic_seq.h"
#include "src/core/lib/promise/detail/promise_like.h"
#include "src/core/lib/promise/detail/seq_state.h"
@ -39,6 +40,7 @@ struct SeqTraits {
return next->Make(std::forward<T>(value));
}
static bool IsOk(const T&) { return true; }
static const char* ErrorString(const T&) { abort(); }
template <typename R>
static R ReturnValue(T&&) {
abort();
@ -57,8 +59,9 @@ struct SeqTraits {
template <typename P, typename... Fs>
class Seq {
public:
explicit Seq(P&& promise, Fs&&... factories)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...) {}
explicit Seq(P&& promise, Fs&&... factories, DebugLocation whence)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...,
whence) {}
auto operator()() { return state_.PollOnce(); }
@ -94,16 +97,68 @@ struct SeqIterResultTraits {
// Pass its result to the third, and run the returned promise.
// etc
// Return the final value.
template <typename... Functors>
promise_detail::Seq<Functors...> Seq(Functors... functors) {
return promise_detail::Seq<Functors...>(std::move(functors)...);
}
template <typename F>
F Seq(F functor) {
return functor;
}
template <typename F0, typename F1>
promise_detail::Seq<F0, F1> Seq(F0 f0, F1 f1, DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1>(std::move(f0), std::move(f1), whence);
}
template <typename F0, typename F1, typename F2>
promise_detail::Seq<F0, F1, F2> Seq(F0 f0, F1 f1, F2 f2,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2>(std::move(f0), std::move(f1),
std::move(f2), whence);
}
template <typename F0, typename F1, typename F2, typename F3>
promise_detail::Seq<F0, F1, F2, F3> Seq(F0 f0, F1 f1, F2 f2, F3 f3,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2, F3>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4>
promise_detail::Seq<F0, F1, F2, F3, F4> Seq(F0 f0, F1 f1, F2 f2, F3 f3, F4 f4,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2, F3, F4>(std::move(f0), std::move(f1),
std::move(f2), std::move(f3),
std::move(f4), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5>
promise_detail::Seq<F0, F1, F2, F3, F4, F5> Seq(F0 f0, F1 f1, F2 f2, F3 f3,
F4 f4, F5 f5,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2, F3, F4, F5>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5, typename F6>
promise_detail::Seq<F0, F1, F2, F3, F4, F5, F6> Seq(F0 f0, F1 f1, F2 f2, F3 f3,
F4 f4, F5 f5, F6 f6,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2, F3, F4, F5, F6>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), std::move(f6), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5, typename F6, typename F7>
promise_detail::Seq<F0, F1, F2, F3, F4, F5, F6, F7> Seq(
F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7,
DebugLocation whence = {}) {
return promise_detail::Seq<F0, F1, F2, F3, F4, F5, F6, F7>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), std::move(f6), std::move(f7), whence);
}
// Execute a sequence of operations of unknown length.
// Asynchronously:
// for (element in (begin, end)) {

@ -45,6 +45,7 @@ struct TrySeqTraitsWithSfinae {
return next->Make(std::forward<T>(value));
}
static bool IsOk(const T&) { return true; }
static const char* ErrorString(const T&) { abort(); }
template <typename R>
static R ReturnValue(T&&) {
abort();
@ -69,6 +70,9 @@ struct TrySeqTraitsWithSfinae<absl::StatusOr<T>> {
return next->Make(std::move(*status));
}
static bool IsOk(const absl::StatusOr<T>& status) { return status.ok(); }
static std::string ErrorString(const absl::StatusOr<T>& status) {
return status.status().ToString();
}
template <typename R>
static R ReturnValue(absl::StatusOr<T>&& status) {
return StatusCast<R>(status.status());
@ -110,6 +114,9 @@ struct TrySeqTraitsWithSfinae<
return next->Make();
}
static bool IsOk(const T& status) { return IsStatusOk(status); }
static std::string ErrorString(const T& status) {
return IsStatusOk(status) ? "OK" : "FAILED";
}
template <typename R>
static R ReturnValue(T&& status) {
return StatusCast<R>(std::move(status));
@ -133,6 +140,9 @@ struct TrySeqTraitsWithSfinae<
return next->Make(TakeValue(std::forward<T>(status)));
}
static bool IsOk(const T& status) { return IsStatusOk(status); }
static std::string ErrorString(const T& status) {
return IsStatusOk(status) ? "OK" : "FAILED";
}
template <typename R>
static R ReturnValue(T&& status) {
GPR_DEBUG_ASSERT(!IsStatusOk(status));
@ -153,6 +163,9 @@ struct TrySeqTraitsWithSfinae<absl::Status> {
return next->Make();
}
static bool IsOk(const absl::Status& status) { return status.ok(); }
static std::string ErrorString(const absl::Status& status) {
return status.ToString();
}
template <typename R>
static R ReturnValue(absl::Status&& status) {
return StatusCast<R>(std::move(status));
@ -171,8 +184,9 @@ using TrySeqTraits = TrySeqTraitsWithSfinae<T>;
template <typename P, typename... Fs>
class TrySeq {
public:
explicit TrySeq(P&& promise, Fs&&... factories)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...) {}
explicit TrySeq(P&& promise, Fs&&... factories, DebugLocation whence)
: state_(std::forward<P>(promise), std::forward<Fs>(factories)...,
whence) {}
auto operator()() { return state_.PollOnce(); }
@ -214,9 +228,66 @@ struct TrySeqIterResultTraits {
// Functors can return StatusOr<> to signal that a value is fed forward, or
// Status to indicate only success/failure. In the case of returning Status,
// the construction functors take no arguments.
template <typename... Functors>
promise_detail::TrySeq<Functors...> TrySeq(Functors... functors) {
return promise_detail::TrySeq<Functors...>(std::move(functors)...);
template <typename F>
F TrySeq(F functor) {
return functor;
}
template <typename F0, typename F1>
promise_detail::TrySeq<F0, F1> TrySeq(F0 f0, F1 f1, DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1>(std::move(f0), std::move(f1), whence);
}
template <typename F0, typename F1, typename F2>
promise_detail::TrySeq<F0, F1, F2> TrySeq(F0 f0, F1 f1, F2 f2,
DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2>(std::move(f0), std::move(f1),
std::move(f2), whence);
}
template <typename F0, typename F1, typename F2, typename F3>
promise_detail::TrySeq<F0, F1, F2, F3> TrySeq(F0 f0, F1 f1, F2 f2, F3 f3,
DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2, F3>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4>
promise_detail::TrySeq<F0, F1, F2, F3, F4> TrySeq(F0 f0, F1 f1, F2 f2, F3 f3,
F4 f4,
DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2, F3, F4>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5>
promise_detail::TrySeq<F0, F1, F2, F3, F4, F5> TrySeq(
F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5, typename F6>
promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6> TrySeq(
F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6,
DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), std::move(f6), whence);
}
template <typename F0, typename F1, typename F2, typename F3, typename F4,
typename F5, typename F6, typename F7>
promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7> TrySeq(
F0 f0, F1 f1, F2 f2, F3 f3, F4 f4, F5 f5, F6 f6, F7 f7,
DebugLocation whence = {}) {
return promise_detail::TrySeq<F0, F1, F2, F3, F4, F5, F6, F7>(
std::move(f0), std::move(f1), std::move(f2), std::move(f3), std::move(f4),
std::move(f5), std::move(f6), std::move(f7), whence);
}
// Try a sequence of operations of unknown length.

@ -83,8 +83,11 @@ union {
% endif
enum class State : uint8_t { ${",".join(f"kState{i}" for i in range(0,n))} };
GPR_NO_UNIQUE_ADDRESS State state = State::kState0;
GPR_NO_UNIQUE_ADDRESS DebugLocation whence;
SeqState(P&& p, ${",".join(f"F{i}&& f{i}" for i in range(0,n-1))}) noexcept {
SeqState(P&& p,
${",".join(f"F{i}&& f{i}" for i in range(0,n-1))},
DebugLocation whence) noexcept: whence(whence) {
Construct(&${"prior."*(n-1)}current_promise, std::forward<P>(p));
% for i in range(0,n-1):
Construct(&${"prior."*(n-1-i)}next_factory, std::forward<F${i}>(f${i}));
@ -106,7 +109,7 @@ tail${i}:
Destruct(&${"prior."*(n-1-i)}next_factory);
% endfor
}
SeqState(const SeqState& other) noexcept : state(other.state) {
SeqState(const SeqState& other) noexcept : state(other.state), whence(other.whence) {
GPR_ASSERT(state == State::kState0);
Construct(&${"prior."*(n-1-i)}current_promise,
other.${"prior."*(n-1-i)}current_promise);
@ -116,7 +119,7 @@ tail${i}:
% endfor
}
SeqState& operator=(const SeqState& other) = delete;
SeqState(SeqState&& other) noexcept : state(other.state) {
SeqState(SeqState&& other) noexcept : state(other.state), whence(other.whence) {
switch (state) {
% for i in range(0,n-1):
case State::kState${i}:
@ -140,13 +143,17 @@ tail${i}:
% for i in range(0,n-1):
case State::kState${i}: {
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "seq[%p]: begin poll step ${i+1}/${n}", this);
gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: begin poll step ${i+1}/${n}", this);
}
auto result = ${"prior."*(n-1-i)}current_promise();
PromiseResult${i}* p = result.value_if_ready();
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "seq[%p]: poll step ${i+1}/${n} gets %s", this,
p != nullptr? (PromiseResultTraits${i}::IsOk(*p)? "ready" : "early-error") : "pending");
gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: poll step ${i+1}/${n} gets %s", this,
p != nullptr
? (PromiseResultTraits${i}::IsOk(*p)
? "ready"
: absl::StrCat("early-error:", PromiseResultTraits${i}::ErrorString(*p)).c_str())
: "pending");
}
if (p == nullptr) return Pending{};
if (!PromiseResultTraits${i}::IsOk(*p)) {
@ -163,11 +170,11 @@ tail${i}:
default:
case State::kState${n-1}: {
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "seq[%p]: begin poll step ${n}/${n}", this);
gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: begin poll step ${n}/${n}", this);
}
auto result = current_promise();
if (grpc_trace_promise_primitives.enabled()) {
gpr_log(GPR_DEBUG, "seq[%p]: poll step ${n}/${n} gets %s", this, result.ready()? "ready" : "pending");
gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: poll step ${n}/${n} gets %s", this, result.ready()? "ready" : "pending");
}
auto* p = result.value_if_ready();
if (p == nullptr) return Pending{};
@ -191,10 +198,12 @@ front_matter = """
#include <utility>
#include "absl/base/attributes.h"
#include "absl/strings/str_cat.h"
#include <grpc/support/log.h>
#include "src/core/lib/gprpp/construct_destruct.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/promise/detail/promise_factory.h"
#include "src/core/lib/promise/detail/promise_like.h"
#include "src/core/lib/promise/poll.h"

Loading…
Cancel
Save