• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION 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  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION ~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  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION SeqState(const SeqState& other) noexcept : state(other.state), whence(other.whence) {
113    DCHECK(state == State::kState0);
114    Construct(&${"prior."*(n-1)}current_promise,
115            other.${"prior."*(n-1)}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  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION SeqState(SeqState&& other) noexcept : state(other.state), whence(other.whence) {
123    DCHECK(state == State::kState0);
124    Construct(&${"prior."*(n-1)}current_promise,
125              std::move(other.${"prior."*(n-1)}current_promise));
126% for i in range(0,n-1):
127    Construct(&${"prior."*(n-1-i)}next_factory,
128              std::move(other.${"prior."*(n-1-i)}next_factory));
129% endfor
130  }
131  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION SeqState& operator=(SeqState&& other) = delete;
132  GPR_ATTRIBUTE_ALWAYS_INLINE_FUNCTION Poll<Result> PollOnce() {
133    switch (state) {
134% for i in range(0,n-1):
135      case State::kState${i}: {
136        GRPC_TRACE_LOG(promise_primitives, INFO).AtLocation(whence.file(), whence.line())
137                << "seq[" << this << "]: begin poll step ${i+1}/${n}";
138        auto result = ${"prior."*(n-1-i)}current_promise();
139        PromiseResult${i}* p = result.value_if_ready();
140        GRPC_TRACE_LOG(promise_primitives, INFO).AtLocation(whence.file(), whence.line())
141                << "seq[" << this << "]: poll step ${i+1}/${n} gets "
142                << (p != nullptr
143                    ? (PromiseResultTraits${i}::IsOk(*p)
144                      ? "ready"
145                      : absl::StrCat("early-error:", PromiseResultTraits${i}::ErrorString(*p)).c_str())
146                    : "pending");
147        if (p == nullptr) return Pending{};
148        if (!PromiseResultTraits${i}::IsOk(*p)) {
149          return PromiseResultTraits${i}::template ReturnValue<Result>(std::move(*p));
150        }
151        Destruct(&${"prior."*(n-1-i)}current_promise);
152        auto next_promise = PromiseResultTraits${i}::CallFactory(&${"prior."*(n-1-i)}next_factory, std::move(*p));
153        Destruct(&${"prior."*(n-1-i)}next_factory);
154        Construct(&${"prior."*(n-2-i)}current_promise, std::move(next_promise));
155        state = State::kState${i+1};
156      }
157      ABSL_FALLTHROUGH_INTENDED;
158% endfor
159      default:
160      case State::kState${n-1}: {
161        GRPC_TRACE_LOG(promise_primitives, INFO).AtLocation(whence.file(), whence.line())
162                << "seq[" << this << "]: begin poll step ${n}/${n}";
163        auto result = current_promise();
164        GRPC_TRACE_LOG(promise_primitives, INFO).AtLocation(whence.file(), whence.line())
165                << "seq[" << this << "]: poll step ${n}/${n} gets "
166                << (result.ready()? "ready" : "pending");
167        auto* p = result.value_if_ready();
168        if (p == nullptr) return Pending{};
169        return Result(std::move(*p));
170      }
171    }
172  }
173};"""
174)
175
176front_matter = """
177#ifndef GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
178#define GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
179
180// This file is generated by tools/codegen/core/gen_seq.py
181
182#include <grpc/support/port_platform.h>
183
184#include <stdint.h>
185
186#include <utility>
187
188#include "absl/log/check.h"
189#include "absl/log/log.h"
190#include "absl/base/attributes.h"
191#include "absl/strings/str_cat.h"
192
193#include "src/core/lib/debug/trace.h"
194#include "src/core/util/construct_destruct.h"
195#include "src/core/util/debug_location.h"
196#include "src/core/lib/promise/detail/promise_factory.h"
197#include "src/core/lib/promise/detail/promise_like.h"
198#include "src/core/lib/promise/poll.h"
199
200// A sequence under some traits for some set of callables P, Fs.
201// P should be a promise-like object that yields a value.
202// Fs... should be promise-factory-like objects that take the value from the
203// previous step and yield a promise. Note that most of the machinery in
204// PromiseFactory exists to make it possible for those promise-factory-like
205// objects to be anything that's convenient.
206// Traits defines how we move from one step to the next. Traits sets up the
207// wrapping and escape handling for the sequence.
208// Promises return wrapped values that the trait can inspect and unwrap before
209// passing them to the next element of the sequence. The trait can
210// also interpret a wrapped value as an escape value, which terminates
211// evaluation of the sequence immediately yielding a result. Traits for type T
212// have the members:
213//  * type UnwrappedType - the type after removing wrapping from T (i.e. for
214//    TrySeq, T=StatusOr<U> yields UnwrappedType=U).
215//  * type WrappedType - the type after adding wrapping if it doesn't already
216//    exist (i.e. for TrySeq if T is not Status/StatusOr/void, then
217//    WrappedType=StatusOr<T>; if T is Status then WrappedType=Status (it's
218//    already wrapped!))
219//  * template <typename Next> void CallFactory(Next* next_factory, T&& value) -
220//    call promise factory next_factory with the result of unwrapping value, and
221//    return the resulting promise.
222//  * template <typename Result, typename RunNext> Poll<Result>
223//    CheckResultAndRunNext(T prior, RunNext run_next) - examine the value of
224//    prior, and decide to escape or continue. If escaping, return the final
225//    sequence value of type Poll<Result>. If continuing, return the value of
226//    run_next(std::move(prior)).
227//
228// A state contains the current promise, and the promise factory to turn the
229// result of the current promise into the next state's promise. We play a shell
230// game such that the prior state and our current promise are kept in a union,
231// and the next promise factory is kept alongside in the state struct.
232// Recursively this guarantees that the next functions get initialized once, and
233// destroyed once, and don't need to be moved around in between, which avoids a
234// potential O(n**2) loop of next factory moves had we used a variant of states
235// here. The very first state does not have a prior state, and so that state has
236// a partial specialization below. The final state does not have a next state;
237// that state is inlined in BasicSeq since that was simpler to type.
238
239namespace grpc_core {
240namespace promise_detail {
241template <template<typename> class Traits, typename P, typename... Fs>
242struct SeqState;
243"""
244
245end_matter = """
246}  // namespace promise_detail
247}  // namespace grpc_core
248
249#endif  // GRPC_SRC_CORE_LIB_PROMISE_DETAIL_SEQ_STATE_H
250"""
251
252
253# utility: print a big comment block into a set of files
254def put_banner(files, banner):
255    for f in files:
256        for line in banner:
257            print("// %s" % line, file=f)
258        print("", file=f)
259
260
261with open(sys.argv[0]) as my_source:
262    copyright = []
263    for line in my_source:
264        if line[0] != "#":
265            break
266    for line in my_source:
267        if line[0] == "#":
268            copyright.append(line)
269            break
270    for line in my_source:
271        if line[0] != "#":
272            break
273        copyright.append(line)
274
275copyright = [line[2:].rstrip() for line in copyright]
276
277with open("src/core/lib/promise/detail/seq_state.h", "w") as f:
278    put_banner([f], copyright)
279    print(front_matter, file=f)
280    for n in range(2, 14):
281        print(seq_state.render(n=n), file=f)
282    print(end_matter, file=f)
283