xref: /aosp_15_r20/external/grpc-grpc/tools/codegen/core/gen_seq.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1#!/usr/bin/env python3
2
3# Copyright 2023 gRPC authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import sys
18
19from mako.template import Template
20
21seq_state = Template(
22    """
23<%def name="decl(promise_name, i, n)">
24using Promise${i} = ${promise_name};
25% if i < n-1:
26using NextFactory${i} = OncePromiseFactory<typename Promise${i}::Result, F${i}>;
27${decl(f"typename NextFactory{i}::Promise", i+1, n)}
28% endif
29</%def>
30
31<%def name="state(i, n)">
32% if i == 0:
33Promise0 current_promise;
34NextFactory0 next_factory;
35% elif i == n-1:
36union {
37    struct { ${state(i-1, n)} } prior;
38    Promise${i} current_promise;
39};
40% else:
41union {
42    struct { ${state(i-1, n)} } prior;
43    P${i} current_promise;
44};
45NextFactory${i} next_factory;
46% endif
47</%def>
48
49template <template<typename> class Traits, typename P, ${",".join(f"typename F{i}" for i in range(0,n-1))}>
50struct SeqState<Traits, P, ${",".join(f"F{i}" for i in range(0,n-1))}> {
51<% name="PromiseLike<P>" %>
52% for i in range(0,n-1):
53using Promise${i} = ${name};
54using PromiseResult${i} = typename Promise${i}::Result;
55using PromiseResultTraits${i} = Traits<PromiseResult${i}>;
56using NextFactory${i} = OncePromiseFactory<typename PromiseResultTraits${i}::UnwrappedType, F${i}>;
57<% name=f"typename NextFactory{i}::Promise" %>\\
58% endfor
59using Promise${n-1} = ${name};
60using PromiseResult${n-1} = typename Promise${n-1}::Result;
61using PromiseResultTraits${n-1} = Traits<PromiseResult${n-1}>;
62using Result = typename PromiseResultTraits${n-1}::WrappedType;
63% if n == 1:
64Promise0 current_promise;
65% else:
66%  for i in range(0,n-1):
67struct Running${i} {
68%   if i != 0:
69union {
70  GPR_NO_UNIQUE_ADDRESS Running${i-1} prior;
71%   endif
72  GPR_NO_UNIQUE_ADDRESS Promise${i} current_promise;
73%   if i != 0:
74};
75%   endif
76GPR_NO_UNIQUE_ADDRESS NextFactory${i} next_factory;
77};
78%  endfor
79union {
80    GPR_NO_UNIQUE_ADDRESS Running${n-2} prior;
81    GPR_NO_UNIQUE_ADDRESS Promise${n-1} current_promise;
82};
83% endif
84  enum class State : uint8_t { ${",".join(f"kState{i}" for i in range(0,n))} };
85  GPR_NO_UNIQUE_ADDRESS State state = State::kState0;
86  GPR_NO_UNIQUE_ADDRESS DebugLocation whence;
87
88  SeqState(P&& p,
89           ${",".join(f"F{i}&& f{i}" for i in range(0,n-1))},
90           DebugLocation whence) noexcept: whence(whence)  {
91    Construct(&${"prior."*(n-1)}current_promise, std::forward<P>(p));
92% for i in range(0,n-1):
93    Construct(&${"prior."*(n-1-i)}next_factory, std::forward<F${i}>(f${i}));
94% endfor
95  }
96  ~SeqState() {
97    switch (state) {
98% for i in range(0,n-1):
99     case State::kState${i}:
100      Destruct(&${"prior."*(n-1-i)}current_promise);
101      goto tail${i};
102% endfor
103     case State::kState${n-1}:
104      Destruct(&current_promise);
105      return;
106    }
107% for i in range(0,n-1):
108tail${i}:
109    Destruct(&${"prior."*(n-1-i)}next_factory);
110% endfor
111  }
112  SeqState(const SeqState& other) noexcept : state(other.state), whence(other.whence) {
113    GPR_ASSERT(state == State::kState0);
114    Construct(&${"prior."*(n-1-i)}current_promise,
115            other.${"prior."*(n-1-i)}current_promise);
116% for i in range(0,n-1):
117    Construct(&${"prior."*(n-1-i)}next_factory,
118              other.${"prior."*(n-1-i)}next_factory);
119% endfor
120  }
121  SeqState& operator=(const SeqState& other) = delete;
122  SeqState(SeqState&& other) noexcept : state(other.state), whence(other.whence) {
123    switch (state) {
124% for i in range(0,n-1):
125     case State::kState${i}:
126      Construct(&${"prior."*(n-1-i)}current_promise,
127                std::move(other.${"prior."*(n-1-i)}current_promise));
128      goto tail${i};
129% endfor
130     case State::kState${n-1}:
131      Construct(&current_promise, std::move(other.current_promise));
132      return;
133    }
134% for i in range(0,n-1):
135tail${i}:
136    Construct(&${"prior."*(n-1-i)}next_factory,
137              std::move(other.${"prior."*(n-1-i)}next_factory));
138% endfor
139  }
140  SeqState& operator=(SeqState&& other) = delete;
141  Poll<Result> PollOnce() {
142    switch (state) {
143% for i in range(0,n-1):
144      case State::kState${i}: {
145        if (grpc_trace_promise_primitives.enabled()) {
146          gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: begin poll step ${i+1}/${n}", this);
147        }
148        auto result = ${"prior."*(n-1-i)}current_promise();
149        PromiseResult${i}* p = result.value_if_ready();
150        if (grpc_trace_promise_primitives.enabled()) {
151          gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: poll step ${i+1}/${n} gets %s", this,
152                  p != nullptr
153                    ? (PromiseResultTraits${i}::IsOk(*p)
154                      ? "ready"
155                      : absl::StrCat("early-error:", PromiseResultTraits${i}::ErrorString(*p)).c_str())
156                    : "pending");
157        }
158        if (p == nullptr) return Pending{};
159        if (!PromiseResultTraits${i}::IsOk(*p)) {
160          return PromiseResultTraits${i}::template ReturnValue<Result>(std::move(*p));
161        }
162        Destruct(&${"prior."*(n-1-i)}current_promise);
163        auto next_promise = PromiseResultTraits${i}::CallFactory(&${"prior."*(n-1-i)}next_factory, std::move(*p));
164        Destruct(&${"prior."*(n-1-i)}next_factory);
165        Construct(&${"prior."*(n-2-i)}current_promise, std::move(next_promise));
166        state = State::kState${i+1};
167      }
168      ABSL_FALLTHROUGH_INTENDED;
169% endfor
170      default:
171      case State::kState${n-1}: {
172        if (grpc_trace_promise_primitives.enabled()) {
173          gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: begin poll step ${n}/${n}", this);
174        }
175        auto result = current_promise();
176        if (grpc_trace_promise_primitives.enabled()) {
177          gpr_log(whence.file(), whence.line(), GPR_LOG_SEVERITY_DEBUG, "seq[%p]: poll step ${n}/${n} gets %s", this, result.ready()? "ready" : "pending");
178        }
179        auto* p = result.value_if_ready();
180        if (p == nullptr) return Pending{};
181        return Result(std::move(*p));
182      }
183    }
184  }
185};"""
186)
187
188front_matter = """
189#ifndef GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
190#define GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
191
192// This file is generated by tools/codegen/core/gen_seq.py
193
194#include <grpc/support/port_platform.h>
195
196#include <stdint.h>
197
198#include <utility>
199
200#include "absl/base/attributes.h"
201#include "absl/strings/str_cat.h"
202
203#include <grpc/support/log.h>
204
205#include "src/core/lib/gprpp/construct_destruct.h"
206#include "src/core/lib/gprpp/debug_location.h"
207#include "src/core/lib/promise/detail/promise_factory.h"
208#include "src/core/lib/promise/detail/promise_like.h"
209#include "src/core/lib/promise/poll.h"
210#include "src/core/lib/promise/trace.h"
211
212// A sequence under some traits for some set of callables P, Fs.
213// P should be a promise-like object that yields a value.
214// Fs... should be promise-factory-like objects that take the value from the
215// previous step and yield a promise. Note that most of the machinery in
216// PromiseFactory exists to make it possible for those promise-factory-like
217// objects to be anything that's convenient.
218// Traits defines how we move from one step to the next. Traits sets up the
219// wrapping and escape handling for the sequence.
220// Promises return wrapped values that the trait can inspect and unwrap before
221// passing them to the next element of the sequence. The trait can
222// also interpret a wrapped value as an escape value, which terminates
223// evaluation of the sequence immediately yielding a result. Traits for type T
224// have the members:
225//  * type UnwrappedType - the type after removing wrapping from T (i.e. for
226//    TrySeq, T=StatusOr<U> yields UnwrappedType=U).
227//  * type WrappedType - the type after adding wrapping if it doesn't already
228//    exist (i.e. for TrySeq if T is not Status/StatusOr/void, then
229//    WrappedType=StatusOr<T>; if T is Status then WrappedType=Status (it's
230//    already wrapped!))
231//  * template <typename Next> void CallFactory(Next* next_factory, T&& value) -
232//    call promise factory next_factory with the result of unwrapping value, and
233//    return the resulting promise.
234//  * template <typename Result, typename RunNext> Poll<Result>
235//    CheckResultAndRunNext(T prior, RunNext run_next) - examine the value of
236//    prior, and decide to escape or continue. If escaping, return the final
237//    sequence value of type Poll<Result>. If continuing, return the value of
238//    run_next(std::move(prior)).
239//
240// A state contains the current promise, and the promise factory to turn the
241// result of the current promise into the next state's promise. We play a shell
242// game such that the prior state and our current promise are kept in a union,
243// and the next promise factory is kept alongside in the state struct.
244// Recursively this guarantees that the next functions get initialized once, and
245// destroyed once, and don't need to be moved around in between, which avoids a
246// potential O(n**2) loop of next factory moves had we used a variant of states
247// here. The very first state does not have a prior state, and so that state has
248// a partial specialization below. The final state does not have a next state;
249// that state is inlined in BasicSeq since that was simpler to type.
250
251namespace grpc_core {
252namespace promise_detail {
253template <template<typename> class Traits, typename P, typename... Fs>
254struct SeqState;
255"""
256
257end_matter = """
258}  // namespace promise_detail
259}  // namespace grpc_core
260
261#endif  // GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
262"""
263
264
265# utility: print a big comment block into a set of files
266def put_banner(files, banner):
267    for f in files:
268        for line in banner:
269            print("// %s" % line, file=f)
270        print("", file=f)
271
272
273with open(sys.argv[0]) as my_source:
274    copyright = []
275    for line in my_source:
276        if line[0] != "#":
277            break
278    for line in my_source:
279        if line[0] == "#":
280            copyright.append(line)
281            break
282    for line in my_source:
283        if line[0] != "#":
284            break
285        copyright.append(line)
286
287copyright = [line[2:].rstrip() for line in copyright]
288
289with open("src/core/lib/promise/detail/seq_state.h", "w") as f:
290    put_banner([f], copyright)
291    print(front_matter, file=f)
292    for n in range(2, 14):
293        print(seq_state.render(n=n), file=f)
294    print(end_matter, file=f)
295