pull/38786/head
Craig Tiller 1 week ago
commit e2b0c59817
  1. 2
      bazel/experiments.bzl
  2. 20
      src/core/ext/transport/chttp2/transport/chttp2_transport.cc
  3. 40
      src/core/ext/transport/chttp2/transport/stream_lists.cc
  4. 4
      src/core/ext/transport/chttp2/transport/writing.cc
  5. 36
      src/core/lib/experiments/experiments.cc
  6. 19
      src/core/lib/experiments/experiments.h
  7. 19
      src/core/lib/experiments/experiments.yaml
  8. 6
      src/core/lib/experiments/rollouts.yaml
  9. 701
      src/core/lib/promise/switch.h
  10. 29
      src/core/load_balancing/rls/rls.cc
  11. 1
      test/cpp/ext/filters/census/stats_plugin_end2end_test.cc
  12. 5
      tools/codegen/core/BUILD
  13. 172
      tools/codegen/core/gen_switch.cc

@ -35,7 +35,6 @@ EXPERIMENT_ENABLES = {
"multiping": "multiping",
"pick_first_new": "pick_first_new",
"posix_ee_skip_grpc_init": "posix_ee_skip_grpc_init",
"prioritize_finished_requests": "prioritize_finished_requests",
"promise_based_http2_client_transport": "promise_based_http2_client_transport",
"promise_based_http2_server_transport": "promise_based_http2_server_transport",
"promise_based_inproc_transport": "promise_based_inproc_transport",
@ -45,7 +44,6 @@ EXPERIMENT_ENABLES = {
"server_listener": "server_listener",
"tcp_frame_size_tuning": "tcp_frame_size_tuning",
"tcp_rcv_lowat": "tcp_rcv_lowat",
"trace_record_callops": "trace_record_callops",
"unconstrained_max_quota_buffer_size": "unconstrained_max_quota_buffer_size",
}

@ -229,9 +229,6 @@ using TaskHandle = ::grpc_event_engine::experimental::EventEngine::TaskHandle;
grpc_core::CallTracerAnnotationInterface* CallTracerIfSampled(
grpc_chttp2_stream* s) {
if (!grpc_core::IsTraceRecordCallopsEnabled()) {
return nullptr;
}
auto* call_tracer =
s->arena->GetContext<grpc_core::CallTracerAnnotationInterface>();
if (call_tracer == nullptr || !call_tracer->IsSampled()) {
@ -242,9 +239,6 @@ grpc_core::CallTracerAnnotationInterface* CallTracerIfSampled(
std::shared_ptr<grpc_core::TcpTracerInterface> TcpTracerIfSampled(
grpc_chttp2_stream* s) {
if (!grpc_core::IsTraceRecordCallopsEnabled()) {
return nullptr;
}
auto* call_attempt_tracer =
s->arena->GetContext<grpc_core::CallTracerInterface>();
if (call_attempt_tracer == nullptr || !call_attempt_tracer->IsSampled()) {
@ -1395,7 +1389,7 @@ static void trace_annotations(grpc_chttp2_stream* s) {
.Add(s->t->flow_control.stats())
.Add(s->flow_control.stats()));
}
} else if (grpc_core::IsTraceRecordCallopsEnabled()) {
} else {
auto* call_tracer = s->arena->GetContext<grpc_core::CallTracerInterface>();
if (call_tracer != nullptr && call_tracer->IsSampled()) {
call_tracer->RecordAnnotation(
@ -1504,18 +1498,6 @@ static void send_message_locked(
absl::OkStatus(),
"fetching_send_message_finished");
} else {
// Buffer hint is used to buffer the message in the transport until the
// write buffer size (specified through GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE) is
// reached. This is to batch writes sent down to tcp. However, if the memory
// pressure is high, disable the buffer hint to flush data down to tcp as
// soon as possible to avoid OOM.
if (grpc_core::IsDisableBufferHintOnHighMemoryPressureEnabled() &&
t->memory_owner.GetPressureInfo().pressure_control_value >= 0.8) {
// Disable write buffer hint if memory pressure is high. The value of 0.8
// is chosen to match the threshold used by the tcp endpoint (in
// allocating memory for socket reads).
op_payload->send_message.flags &= ~GRPC_WRITE_BUFFER_HINT;
}
flags = op_payload->send_message.flags;
uint8_t* frame_hdr = grpc_slice_buffer_tiny_add(&s->flow_controlled_buffer,
GRPC_HEADER_SIZE_IN_BYTES);

@ -128,26 +128,6 @@ static void stream_list_add_tail(grpc_chttp2_transport* t,
<< "]: add to " << stream_list_id_string(id);
}
static void stream_list_add_head(grpc_chttp2_transport* t,
grpc_chttp2_stream* s,
grpc_chttp2_stream_list_id id) {
grpc_chttp2_stream* old_head;
CHECK(!s->included.is_set(id));
old_head = t->lists[id].head;
s->links[id].next = old_head;
s->links[id].prev = nullptr;
if (old_head) {
old_head->links[id].prev = s;
} else {
t->lists[id].tail = s;
}
t->lists[id].head = s;
s->included.set(id);
GRPC_TRACE_LOG(http2_stream_state, INFO)
<< t << "[" << s->id << "][" << (t->is_client ? "cli" : "svr")
<< "]: add to " << stream_list_id_string(id);
}
static bool stream_list_add(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
grpc_chttp2_stream_list_id id) {
if (s->included.is_set(id)) {
@ -157,24 +137,11 @@ static bool stream_list_add(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
return true;
}
static bool stream_list_prepend(grpc_chttp2_transport* t, grpc_chttp2_stream* s,
grpc_chttp2_stream_list_id id) {
if (s->included.is_set(id)) {
return false;
}
stream_list_add_head(t, s, id);
return true;
}
// wrappers for specializations
bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport* t,
grpc_chttp2_stream* s) {
CHECK_NE(s->id, 0u);
if (grpc_core::IsPrioritizeFinishedRequestsEnabled() &&
s->send_trailing_metadata != nullptr) {
return stream_list_prepend(t, s, GRPC_CHTTP2_LIST_WRITABLE);
}
return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITABLE);
}
@ -219,12 +186,7 @@ void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport* t,
void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport* t,
grpc_chttp2_stream* s) {
if (grpc_core::IsPrioritizeFinishedRequestsEnabled() &&
s->send_trailing_metadata != nullptr) {
stream_list_prepend(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
} else {
stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
}
stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT);
}
bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport* t,

@ -484,7 +484,7 @@ class StreamWriteContext {
.Add(s_->flow_control.stats())
.Add(write_stats));
}
} else if (grpc_core::IsTraceRecordCallopsEnabled()) {
} else {
auto* call_tracer =
s_->arena->GetContext<grpc_core::CallTracerInterface>();
if (call_tracer != nullptr && call_tracer->IsSampled()) {
@ -646,7 +646,7 @@ class StreamWriteContext {
.Add(s_->t->flow_control.stats())
.Add(s_->flow_control.stats()));
}
} else if (grpc_core::IsTraceRecordCallopsEnabled()) {
} else {
auto* call_tracer =
s_->arena->GetContext<grpc_core::CallTracerInterface>();
if (call_tracer != nullptr && call_tracer->IsSampled()) {

@ -89,10 +89,6 @@ const char* const description_posix_ee_skip_grpc_init =
"Prevent the PosixEventEngine from calling grpc_init & grpc_shutdown on "
"creation and destruction.";
const char* const additional_constraints_posix_ee_skip_grpc_init = "{}";
const char* const description_prioritize_finished_requests =
"Prioritize flushing out finished requests over other in-flight requests "
"during transport writes.";
const char* const additional_constraints_prioritize_finished_requests = "{}";
const char* const description_promise_based_http2_client_transport =
"Use promises for the http2 client transport. We have kept client and "
"server transport experiments separate to help with smoother roll outs and "
@ -130,9 +126,6 @@ const char* const additional_constraints_tcp_frame_size_tuning = "{}";
const char* const description_tcp_rcv_lowat =
"Use SO_RCVLOWAT to avoid wakeups on the read path.";
const char* const additional_constraints_tcp_rcv_lowat = "{}";
const char* const description_trace_record_callops =
"Enables tracing of call batch initiation and completion.";
const char* const additional_constraints_trace_record_callops = "{}";
const char* const description_unconstrained_max_quota_buffer_size =
"Discard the cap on the max free pool size for one memory allocator";
const char* const additional_constraints_unconstrained_max_quota_buffer_size =
@ -185,9 +178,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_pick_first_new, nullptr, 0, true, true},
{"posix_ee_skip_grpc_init", description_posix_ee_skip_grpc_init,
additional_constraints_posix_ee_skip_grpc_init, nullptr, 0, false, true},
{"prioritize_finished_requests", description_prioritize_finished_requests,
additional_constraints_prioritize_finished_requests, nullptr, 0, false,
true},
{"promise_based_http2_client_transport",
description_promise_based_http2_client_transport,
additional_constraints_promise_based_http2_client_transport, nullptr, 0,
@ -214,8 +204,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_tcp_frame_size_tuning, nullptr, 0, false, true},
{"tcp_rcv_lowat", description_tcp_rcv_lowat,
additional_constraints_tcp_rcv_lowat, nullptr, 0, false, true},
{"trace_record_callops", description_trace_record_callops,
additional_constraints_trace_record_callops, nullptr, 0, true, true},
{"unconstrained_max_quota_buffer_size",
description_unconstrained_max_quota_buffer_size,
additional_constraints_unconstrained_max_quota_buffer_size, nullptr, 0,
@ -292,10 +280,6 @@ const char* const description_posix_ee_skip_grpc_init =
"Prevent the PosixEventEngine from calling grpc_init & grpc_shutdown on "
"creation and destruction.";
const char* const additional_constraints_posix_ee_skip_grpc_init = "{}";
const char* const description_prioritize_finished_requests =
"Prioritize flushing out finished requests over other in-flight requests "
"during transport writes.";
const char* const additional_constraints_prioritize_finished_requests = "{}";
const char* const description_promise_based_http2_client_transport =
"Use promises for the http2 client transport. We have kept client and "
"server transport experiments separate to help with smoother roll outs and "
@ -333,9 +317,6 @@ const char* const additional_constraints_tcp_frame_size_tuning = "{}";
const char* const description_tcp_rcv_lowat =
"Use SO_RCVLOWAT to avoid wakeups on the read path.";
const char* const additional_constraints_tcp_rcv_lowat = "{}";
const char* const description_trace_record_callops =
"Enables tracing of call batch initiation and completion.";
const char* const additional_constraints_trace_record_callops = "{}";
const char* const description_unconstrained_max_quota_buffer_size =
"Discard the cap on the max free pool size for one memory allocator";
const char* const additional_constraints_unconstrained_max_quota_buffer_size =
@ -388,9 +369,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_pick_first_new, nullptr, 0, true, true},
{"posix_ee_skip_grpc_init", description_posix_ee_skip_grpc_init,
additional_constraints_posix_ee_skip_grpc_init, nullptr, 0, false, true},
{"prioritize_finished_requests", description_prioritize_finished_requests,
additional_constraints_prioritize_finished_requests, nullptr, 0, false,
true},
{"promise_based_http2_client_transport",
description_promise_based_http2_client_transport,
additional_constraints_promise_based_http2_client_transport, nullptr, 0,
@ -417,8 +395,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_tcp_frame_size_tuning, nullptr, 0, false, true},
{"tcp_rcv_lowat", description_tcp_rcv_lowat,
additional_constraints_tcp_rcv_lowat, nullptr, 0, false, true},
{"trace_record_callops", description_trace_record_callops,
additional_constraints_trace_record_callops, nullptr, 0, true, true},
{"unconstrained_max_quota_buffer_size",
description_unconstrained_max_quota_buffer_size,
additional_constraints_unconstrained_max_quota_buffer_size, nullptr, 0,
@ -495,10 +471,6 @@ const char* const description_posix_ee_skip_grpc_init =
"Prevent the PosixEventEngine from calling grpc_init & grpc_shutdown on "
"creation and destruction.";
const char* const additional_constraints_posix_ee_skip_grpc_init = "{}";
const char* const description_prioritize_finished_requests =
"Prioritize flushing out finished requests over other in-flight requests "
"during transport writes.";
const char* const additional_constraints_prioritize_finished_requests = "{}";
const char* const description_promise_based_http2_client_transport =
"Use promises for the http2 client transport. We have kept client and "
"server transport experiments separate to help with smoother roll outs and "
@ -536,9 +508,6 @@ const char* const additional_constraints_tcp_frame_size_tuning = "{}";
const char* const description_tcp_rcv_lowat =
"Use SO_RCVLOWAT to avoid wakeups on the read path.";
const char* const additional_constraints_tcp_rcv_lowat = "{}";
const char* const description_trace_record_callops =
"Enables tracing of call batch initiation and completion.";
const char* const additional_constraints_trace_record_callops = "{}";
const char* const description_unconstrained_max_quota_buffer_size =
"Discard the cap on the max free pool size for one memory allocator";
const char* const additional_constraints_unconstrained_max_quota_buffer_size =
@ -591,9 +560,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_pick_first_new, nullptr, 0, true, true},
{"posix_ee_skip_grpc_init", description_posix_ee_skip_grpc_init,
additional_constraints_posix_ee_skip_grpc_init, nullptr, 0, false, true},
{"prioritize_finished_requests", description_prioritize_finished_requests,
additional_constraints_prioritize_finished_requests, nullptr, 0, false,
true},
{"promise_based_http2_client_transport",
description_promise_based_http2_client_transport,
additional_constraints_promise_based_http2_client_transport, nullptr, 0,
@ -620,8 +586,6 @@ const ExperimentMetadata g_experiment_metadata[] = {
additional_constraints_tcp_frame_size_tuning, nullptr, 0, false, true},
{"tcp_rcv_lowat", description_tcp_rcv_lowat,
additional_constraints_tcp_rcv_lowat, nullptr, 0, false, true},
{"trace_record_callops", description_trace_record_callops,
additional_constraints_trace_record_callops, nullptr, 0, true, true},
{"unconstrained_max_quota_buffer_size",
description_unconstrained_max_quota_buffer_size,
additional_constraints_unconstrained_max_quota_buffer_size, nullptr, 0,

@ -81,7 +81,6 @@ inline bool IsMultipingEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_PICK_FIRST_NEW
inline bool IsPickFirstNewEnabled() { return true; }
inline bool IsPosixEeSkipGrpcInitEnabled() { return false; }
inline bool IsPrioritizeFinishedRequestsEnabled() { return false; }
inline bool IsPromiseBasedHttp2ClientTransportEnabled() { return false; }
inline bool IsPromiseBasedHttp2ServerTransportEnabled() { return false; }
inline bool IsPromiseBasedInprocTransportEnabled() { return false; }
@ -92,8 +91,6 @@ inline bool IsScheduleCancellationOverWriteEnabled() { return false; }
inline bool IsServerListenerEnabled() { return true; }
inline bool IsTcpFrameSizeTuningEnabled() { return false; }
inline bool IsTcpRcvLowatEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_TRACE_RECORD_CALLOPS
inline bool IsTraceRecordCallopsEnabled() { return true; }
inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() { return false; }
#elif defined(GPR_WINDOWS)
@ -124,7 +121,6 @@ inline bool IsMultipingEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_PICK_FIRST_NEW
inline bool IsPickFirstNewEnabled() { return true; }
inline bool IsPosixEeSkipGrpcInitEnabled() { return false; }
inline bool IsPrioritizeFinishedRequestsEnabled() { return false; }
inline bool IsPromiseBasedHttp2ClientTransportEnabled() { return false; }
inline bool IsPromiseBasedHttp2ServerTransportEnabled() { return false; }
inline bool IsPromiseBasedInprocTransportEnabled() { return false; }
@ -135,8 +131,6 @@ inline bool IsScheduleCancellationOverWriteEnabled() { return false; }
inline bool IsServerListenerEnabled() { return true; }
inline bool IsTcpFrameSizeTuningEnabled() { return false; }
inline bool IsTcpRcvLowatEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_TRACE_RECORD_CALLOPS
inline bool IsTraceRecordCallopsEnabled() { return true; }
inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() { return false; }
#else
@ -167,7 +161,6 @@ inline bool IsMultipingEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_PICK_FIRST_NEW
inline bool IsPickFirstNewEnabled() { return true; }
inline bool IsPosixEeSkipGrpcInitEnabled() { return false; }
inline bool IsPrioritizeFinishedRequestsEnabled() { return false; }
inline bool IsPromiseBasedHttp2ClientTransportEnabled() { return false; }
inline bool IsPromiseBasedHttp2ServerTransportEnabled() { return false; }
inline bool IsPromiseBasedInprocTransportEnabled() { return false; }
@ -178,8 +171,6 @@ inline bool IsScheduleCancellationOverWriteEnabled() { return false; }
inline bool IsServerListenerEnabled() { return true; }
inline bool IsTcpFrameSizeTuningEnabled() { return false; }
inline bool IsTcpRcvLowatEnabled() { return false; }
#define GRPC_EXPERIMENT_IS_INCLUDED_TRACE_RECORD_CALLOPS
inline bool IsTraceRecordCallopsEnabled() { return true; }
inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() { return false; }
#endif
@ -203,7 +194,6 @@ enum ExperimentIds {
kExperimentIdMultiping,
kExperimentIdPickFirstNew,
kExperimentIdPosixEeSkipGrpcInit,
kExperimentIdPrioritizeFinishedRequests,
kExperimentIdPromiseBasedHttp2ClientTransport,
kExperimentIdPromiseBasedHttp2ServerTransport,
kExperimentIdPromiseBasedInprocTransport,
@ -213,7 +203,6 @@ enum ExperimentIds {
kExperimentIdServerListener,
kExperimentIdTcpFrameSizeTuning,
kExperimentIdTcpRcvLowat,
kExperimentIdTraceRecordCallops,
kExperimentIdUnconstrainedMaxQuotaBufferSize,
kNumExperiments
};
@ -290,10 +279,6 @@ inline bool IsPickFirstNewEnabled() {
inline bool IsPosixEeSkipGrpcInitEnabled() {
return IsExperimentEnabled<kExperimentIdPosixEeSkipGrpcInit>();
}
#define GRPC_EXPERIMENT_IS_INCLUDED_PRIORITIZE_FINISHED_REQUESTS
inline bool IsPrioritizeFinishedRequestsEnabled() {
return IsExperimentEnabled<kExperimentIdPrioritizeFinishedRequests>();
}
#define GRPC_EXPERIMENT_IS_INCLUDED_PROMISE_BASED_HTTP2_CLIENT_TRANSPORT
inline bool IsPromiseBasedHttp2ClientTransportEnabled() {
return IsExperimentEnabled<kExperimentIdPromiseBasedHttp2ClientTransport>();
@ -330,10 +315,6 @@ inline bool IsTcpFrameSizeTuningEnabled() {
inline bool IsTcpRcvLowatEnabled() {
return IsExperimentEnabled<kExperimentIdTcpRcvLowat>();
}
#define GRPC_EXPERIMENT_IS_INCLUDED_TRACE_RECORD_CALLOPS
inline bool IsTraceRecordCallopsEnabled() {
return IsExperimentEnabled<kExperimentIdTraceRecordCallops>();
}
#define GRPC_EXPERIMENT_IS_INCLUDED_UNCONSTRAINED_MAX_QUOTA_BUFFER_SIZE
inline bool IsUnconstrainedMaxQuotaBufferSizeEnabled() {
return IsExperimentEnabled<kExperimentIdUnconstrainedMaxQuotaBufferSize>();

@ -108,7 +108,7 @@
description:
Avoid explicitly cancelling the keepalive timer. Instead adjust the callback to re-schedule
itself to the next ping interval.
expiry: 2025/03/01
expiry: 2025/06/01
owner: vigneshbabu@google.com
test_tags: []
- name: local_connector_secure
@ -145,12 +145,6 @@
expiry: 2025/03/02
owner: hork@google.com
test_tags: ["core_end2end_test", "cpp_end2end_test"]
- name: prioritize_finished_requests
description: Prioritize flushing out finished requests over other in-flight
requests during transport writes.
expiry: 2025/03/01
owner: vigneshbabu@google.com
test_tags: []
- name: promise_based_http2_client_transport
description:
Use promises for the http2 client transport. We have kept client and
@ -189,7 +183,7 @@
test_tags: []
- name: schedule_cancellation_over_write
description: Allow cancellation op to be scheduled over a write
expiry: 2025/03/01
expiry: 2025/06/01
owner: vigneshbabu@google.com
test_tags: []
- name: server_listener
@ -203,19 +197,14 @@
TCP would not indicate completion of a read operation until a specified
number of bytes have been read over the socket.
Buffers are also allocated according to estimated RPC sizes.
expiry: 2025/03/01
expiry: 2025/06/01
owner: vigneshbabu@google.com
test_tags: ["endpoint_test", "flow_control_test"]
- name: tcp_rcv_lowat
description: Use SO_RCVLOWAT to avoid wakeups on the read path.
expiry: 2025/03/01
expiry: 2025/06/01
owner: vigneshbabu@google.com
test_tags: ["endpoint_test", "flow_control_test"]
- name: trace_record_callops
description: Enables tracing of call batch initiation and completion.
expiry: 2025/01/30
owner: vigneshbabu@google.com
test_tags: []
- name: unconstrained_max_quota_buffer_size
description: Discard the cap on the max free pool size for one memory allocator
expiry: 2025/09/03

@ -46,8 +46,6 @@
default: true
- name: call_v3
default: false
- name: disable_buffer_hint_on_high_memory_pressure
default: false
- name: event_engine_callback_cq
default: true
- name: event_engine_client
@ -82,8 +80,6 @@
default: true
- name: posix_ee_skip_grpc_init
default: false
- name: prioritize_finished_requests
default: false
- name: promise_based_http2_client_transport
default: false
- name: promise_based_http2_server_transport
@ -100,7 +96,5 @@
default: false
- name: tcp_rcv_lowat
default: false
- name: trace_record_callops
default: true
- name: unconstrained_max_quota_buffer_size
default: false

@ -1,4 +1,4 @@
// Copyright 2023 gRPC authors.
// Copyright 2025 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -73,14 +73,12 @@ namespace grpc_core {
// discriminator value. However this should be avoided as it could lead to bugs.
namespace promise_detail {
template <auto kDiscriminator, typename F>
template <auto D, typename F>
struct Case {
using Factory = OncePromiseFactory<void, F>;
explicit Case(F f) : factory(std::move(f)) {}
Factory factory;
static bool Matches(decltype(kDiscriminator) value) {
return value == kDiscriminator;
}
static constexpr const decltype(D) kDiscriminator = D;
};
template <typename F>
@ -89,16 +87,689 @@ struct Default {
explicit Default(F f) : factory(std::move(f)) {}
Factory factory;
};
template <typename Promise, typename D, typename F>
Promise ConstructSwitchPromise(D, Default<F>& def) {
return def.factory.Make();
template <typename Promise, typename D, typename FD>
Promise ConstructSwitchPromise(D discriminator, Default<FD>& default_case) {
switch (discriminator) {
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename Case, typename... OtherCases>
Promise ConstructSwitchPromise(D discriminator, Case& c, OtherCases&... cs) {
if (Case::Matches(discriminator)) return c.factory.Make();
return ConstructSwitchPromise<Promise>(discriminator, cs...);
template <typename Promise, typename D, typename F0, D d0, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Case<d13, F13>& case_13,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename F14, D d14,
typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Case<d13, F13>& case_13, Case<d14, F14>& case_14,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
case Case<d14, F14>::kDiscriminator:
return case_14.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename F14, D d14,
typename F15, D d15, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Case<d13, F13>& case_13, Case<d14, F14>& case_14,
Case<d15, F15>& case_15,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
case Case<d14, F14>::kDiscriminator:
return case_14.factory.Make();
case Case<d15, F15>::kDiscriminator:
return case_15.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename F14, D d14,
typename F15, D d15, typename F16, D d16, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Case<d13, F13>& case_13, Case<d14, F14>& case_14,
Case<d15, F15>& case_15, Case<d16, F16>& case_16,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
case Case<d14, F14>::kDiscriminator:
return case_14.factory.Make();
case Case<d15, F15>::kDiscriminator:
return case_15.factory.Make();
case Case<d16, F16>::kDiscriminator:
return case_16.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename F14, D d14,
typename F15, D d15, typename F16, D d16, typename F17, D d17,
typename FD>
Promise ConstructSwitchPromise(
D discriminator, Case<d0, F0>& case_0, Case<d1, F1>& case_1,
Case<d2, F2>& case_2, Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6, Case<d7, F7>& case_7,
Case<d8, F8>& case_8, Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12, Case<d13, F13>& case_13,
Case<d14, F14>& case_14, Case<d15, F15>& case_15, Case<d16, F16>& case_16,
Case<d17, F17>& case_17, Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
case Case<d14, F14>::kDiscriminator:
return case_14.factory.Make();
case Case<d15, F15>::kDiscriminator:
return case_15.factory.Make();
case Case<d16, F16>::kDiscriminator:
return case_16.factory.Make();
case Case<d17, F17>::kDiscriminator:
return case_17.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename Promise, typename D, typename F0, D d0, typename F1, D d1,
typename F2, D d2, typename F3, D d3, typename F4, D d4, typename F5,
D d5, typename F6, D d6, typename F7, D d7, typename F8, D d8,
typename F9, D d9, typename F10, D d10, typename F11, D d11,
typename F12, D d12, typename F13, D d13, typename F14, D d14,
typename F15, D d15, typename F16, D d16, typename F17, D d17,
typename F18, D d18, typename FD>
Promise ConstructSwitchPromise(D discriminator, Case<d0, F0>& case_0,
Case<d1, F1>& case_1, Case<d2, F2>& case_2,
Case<d3, F3>& case_3, Case<d4, F4>& case_4,
Case<d5, F5>& case_5, Case<d6, F6>& case_6,
Case<d7, F7>& case_7, Case<d8, F8>& case_8,
Case<d9, F9>& case_9, Case<d10, F10>& case_10,
Case<d11, F11>& case_11, Case<d12, F12>& case_12,
Case<d13, F13>& case_13, Case<d14, F14>& case_14,
Case<d15, F15>& case_15, Case<d16, F16>& case_16,
Case<d17, F17>& case_17, Case<d18, F18>& case_18,
Default<FD>& default_case) {
switch (discriminator) {
case Case<d0, F0>::kDiscriminator:
return case_0.factory.Make();
case Case<d1, F1>::kDiscriminator:
return case_1.factory.Make();
case Case<d2, F2>::kDiscriminator:
return case_2.factory.Make();
case Case<d3, F3>::kDiscriminator:
return case_3.factory.Make();
case Case<d4, F4>::kDiscriminator:
return case_4.factory.Make();
case Case<d5, F5>::kDiscriminator:
return case_5.factory.Make();
case Case<d6, F6>::kDiscriminator:
return case_6.factory.Make();
case Case<d7, F7>::kDiscriminator:
return case_7.factory.Make();
case Case<d8, F8>::kDiscriminator:
return case_8.factory.Make();
case Case<d9, F9>::kDiscriminator:
return case_9.factory.Make();
case Case<d10, F10>::kDiscriminator:
return case_10.factory.Make();
case Case<d11, F11>::kDiscriminator:
return case_11.factory.Make();
case Case<d12, F12>::kDiscriminator:
return case_12.factory.Make();
case Case<d13, F13>::kDiscriminator:
return case_13.factory.Make();
case Case<d14, F14>::kDiscriminator:
return case_14.factory.Make();
case Case<d15, F15>::kDiscriminator:
return case_15.factory.Make();
case Case<d16, F16>::kDiscriminator:
return case_16.factory.Make();
case Case<d17, F17>::kDiscriminator:
return case_17.factory.Make();
case Case<d18, F18>::kDiscriminator:
return case_18.factory.Make();
default:
return default_case.factory.Make();
}
}
template <typename D, typename... Cases>
@ -120,8 +791,6 @@ auto Default(PromiseFactory f) {
return promise_detail::Default<PromiseFactory>{std::move(f)};
}
// TODO(ctiller): consider writing a code-generator like we do for seq/join
// so that this lowers into a C switch statement.
template <typename D, typename... C>
auto Switch(D discriminator, C... cases) {
return promise_detail::SwitchImpl(discriminator, cases...);

@ -684,12 +684,10 @@ class RlsLb final : public LoadBalancingPolicy {
void ShutdownLocked() override;
// Returns a new picker to the channel to trigger reprocessing of
// pending picks. Schedules the actual picker update on the ExecCtx
// to be run later, so it's safe to invoke this while holding the lock.
// Schedules a call to UpdatePickerLocked() on the WorkSerializer.
// The call will be run asynchronously, so it's safe to invoke this
// while holding the lock.
void UpdatePickerAsync();
// Hops into work serializer and calls UpdatePickerLocked().
static void UpdatePickerCallback(void* arg, grpc_error_handle error);
// Updates the picker in the work serializer.
void UpdatePickerLocked() ABSL_LOCKS_EXCLUDED(&mu_);
@ -2057,23 +2055,10 @@ void RlsLb::ShutdownLocked() {
}
void RlsLb::UpdatePickerAsync() {
// Run via the ExecCtx, since the caller may be holding the lock, and
// we don't want to be doing that when we hop into the WorkSerializer,
// in case the WorkSerializer callback happens to run inline.
ExecCtx::Run(
DEBUG_LOCATION,
GRPC_CLOSURE_CREATE(UpdatePickerCallback,
Ref(DEBUG_LOCATION, "UpdatePickerCallback").release(),
grpc_schedule_on_exec_ctx),
absl::OkStatus());
}
void RlsLb::UpdatePickerCallback(void* arg, grpc_error_handle /*error*/) {
auto* rls_lb = static_cast<RlsLb*>(arg);
rls_lb->work_serializer()->Run([rls_lb]() {
RefCountedPtr<RlsLb> lb_policy(rls_lb);
lb_policy->UpdatePickerLocked();
lb_policy.reset(DEBUG_LOCATION, "UpdatePickerCallback");
work_serializer()->Run([self = RefAsSubclass<RlsLb>(
DEBUG_LOCATION, "UpdatePickerAsync")]() mutable {
self->UpdatePickerLocked();
self.reset(DEBUG_LOCATION, "UpdatePickerAsync");
});
}

@ -1110,7 +1110,6 @@ TEST(StatsPluginDeclarationTest, Declarations) {
int main(int argc, char** argv) {
grpc::testing::TestEnvironment env(&argc, argv);
grpc_core::ForceEnableExperiment("trace_record_callops", true);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -39,3 +39,8 @@ grpc_cc_binary(
"//src/core:match",
],
)
grpc_cc_binary(
name = "gen_switch",
srcs = ["gen_switch.cc"],
)

@ -0,0 +1,172 @@
// Copyright 2025 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string>
const char* kPrefix = R"(// Copyright 2025 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
#define GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
#include <grpc/support/port_platform.h>
#include <memory>
#include <utility>
#include "src/core/lib/promise/detail/promise_factory.h"
#include "src/core/lib/promise/detail/promise_variant.h"
#include "src/core/lib/promise/if.h"
#include "src/core/util/crash.h"
namespace grpc_core {
// Switch promise combinator
//
// Input :
//
// 1. The first input is the Switch discriminator. Only data types that can be
// used with a C++ switch statement can be passed as the discriminator.
// 2. Then we can pass zero or more objects of type Case as inputs. The Case
// object is a promise factory.
// 3. One object of type Default.
//
// Returns :
// Returns a promise that is chosen based on the discriminator.
//
// How it works :
// Given a discriminator, the Switch combinator tries to find a matching Case.
// If a matching Case is found, then the promise corresponding to the matching
// Case is returned.
// If a matching Case is not found, the promise corresponding to the Default is
// returned.
//
// Example :
//
// TEST(SwitchTest, Sample) {
// auto test_switch = [](int discriminator) {
// return Switch(discriminator,
// Case<1>([] { return 100; }),
// Case<2>([] { return 200; }),
// Case<3>([]() -> Poll<int> { return Pending{}; }),
// Default([] { return -1; }));
// };
// EXPECT_EQ(test_switch(1)(), Poll<int>(100));
// EXPECT_EQ(test_switch(2)(), Poll<int>(200));
// EXPECT_EQ(test_switch(3)(), Poll<int>(Pending{}));
// EXPECT_EQ(test_switch(4)(), Poll<int>(-1));
// }
//
// All Case objects and the Default object must have the same Poll<T> return
// type.
//
// The fallthrough mechanism is present in C++ switch statements is NOT present
// in the Switch promise combinator.
//
// Our code currently permits you to create multiple cases for the same
// discriminator value. However this should be avoided as it could lead to bugs.
namespace promise_detail {
template <auto D, typename F>
struct Case {
using Factory = OncePromiseFactory<void, F>;
explicit Case(F f) : factory(std::move(f)) {}
Factory factory;
static constexpr const decltype(D) kDiscriminator = D;
};
template <typename F>
struct Default {
using Factory = OncePromiseFactory<void, F>;
explicit Default(F f) : factory(std::move(f)) {}
Factory factory;
};
)";
static const char* kSuffix = R"(
template <typename D, typename... Cases>
auto SwitchImpl(D discriminator, Cases&... cases) {
using Promise = std::variant<typename Cases::Factory::Promise...>;
return PromiseVariant<Promise>(
ConstructSwitchPromise<Promise>(discriminator, cases...));
}
} // namespace promise_detail
template <auto kDiscriminator, typename PromiseFactory>
auto Case(PromiseFactory f) {
return promise_detail::Case<kDiscriminator, PromiseFactory>{std::move(f)};
}
template <typename PromiseFactory>
auto Default(PromiseFactory f) {
return promise_detail::Default<PromiseFactory>{std::move(f)};
}
template <typename D, typename... C>
auto Switch(D discriminator, C... cases) {
return promise_detail::SwitchImpl(discriminator, cases...);
}
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_PROMISE_SWITCH_H
)";
std::string Switch(int n) {
std::string out = "template <typename Promise, typename D";
for (int i=0; i<n; i++) {
std::string istr = std::to_string(i);
out += ", typename F" + istr + ", D d" + istr;
}
out += ", typename FD>\n";
out += "Promise ConstructSwitchPromise(D discriminator";
for (int i=0; i<n; i++) {
std::string istr = std::to_string(i);
out += ", Case<d"+istr+", F" + istr + ">& case_" + istr;
}
out += ", Default<FD>& default_case) {\n";
out += " switch (discriminator) {\n";
for (int i=0; i<n; i++) {
std::string istr = std::to_string(i);
out += " case Case<d"+istr+", F" + istr + ">::kDiscriminator: return case_" + istr + ".factory.Make();\n";
}
out += " default: return default_case.factory.Make();\n";
out += " }";
out += "}\n";
return out;
}
int main() {
std::string out = kPrefix;
for (int i=0; i<20; i++) {
out += Switch(i);
}
out += kSuffix;
printf("%s\n", out.c_str());
return 0;
}
Loading…
Cancel
Save