Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 1 | // Copyright 2019 The SwiftShader Authors. All Rights Reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "Reactor.hpp" |
| 16 | |
| 17 | #include <memory> |
| 18 | |
| 19 | #ifndef rr_ReactorCoroutine_hpp |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 20 | # define rr_ReactorCoroutine_hpp |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 21 | |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 22 | namespace rr { |
| 23 | |
| 24 | // Base class for the template Stream<T> |
| 25 | class StreamBase |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 26 | { |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 27 | protected: |
| 28 | StreamBase(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle) |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 29 | : routine(routine) |
| 30 | , handle(handle) |
| 31 | {} |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 32 | |
| 33 | ~StreamBase() |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 34 | { |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 35 | auto pfn = (Nucleus::CoroutineDestroy *)routine->getEntry(Nucleus::CoroutineEntryDestroy); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 36 | pfn(handle); |
| 37 | } |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 38 | |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 39 | bool await(void *out) |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 40 | { |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 41 | auto pfn = (Nucleus::CoroutineAwait *)routine->getEntry(Nucleus::CoroutineEntryAwait); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 42 | return pfn(handle, out); |
| 43 | } |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 44 | |
| 45 | private: |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 46 | std::shared_ptr<Routine> routine; |
| 47 | Nucleus::CoroutineHandle handle; |
| 48 | }; |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 49 | |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 50 | // Stream is the interface to a running Coroutine instance. |
| 51 | // A Coroutine may Yield() values of type T, which can be retrieved with |
| 52 | // await(). |
| 53 | template<typename T> |
| 54 | class Stream : public StreamBase |
| 55 | { |
| 56 | public: |
| 57 | inline Stream(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle) |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 58 | : StreamBase(routine, handle) |
| 59 | {} |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 60 | |
| 61 | // await() retrieves the next yielded value from the coroutine. |
| 62 | // Returns true if the coroutine yieled a value and out was assigned a |
| 63 | // new value. If await() returns false, the coroutine has finished |
| 64 | // execution and await() will return false for all future calls. |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 65 | inline bool await(T &out) { return StreamBase::await(&out); } |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 66 | }; |
| 67 | |
| 68 | template<typename FunctionType> |
| 69 | class Coroutine; |
| 70 | |
| 71 | // Coroutine constructs a reactor Coroutine function. |
| 72 | // rr::Coroutine is similar to rr::Function in that it builds a new |
| 73 | // executable function, but Coroutines have the following differences: |
| 74 | // (1) Coroutines do not support Return() statements. |
| 75 | // (2) Coroutines support Yield() statements to suspend execution of the |
| 76 | // coroutine and pass a value up to the caller. Yield can be called |
| 77 | // multiple times in a single execution of a coroutine. |
| 78 | // (3) The template argument T to Coroutine<T> is a C-style function |
| 79 | // signature. |
| 80 | // (4) Coroutine::operator() returns a rr::Stream<T> instead of an |
| 81 | // rr::Routine. |
| 82 | // (5) operator() starts execution of the coroutine immediately. |
| 83 | // (6) operator() uses the Coroutine's template function signature to |
| 84 | // ensure the argument types match the generated function signature. |
| 85 | // |
| 86 | // Example usage: |
| 87 | // |
| 88 | // // Build the coroutine function |
| 89 | // Coroutine<int()> coroutine; |
| 90 | // { |
| 91 | // Yield(Int(0)); |
| 92 | // Yield(Int(1)); |
| 93 | // Int current = 1; |
| 94 | // Int next = 1; |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 95 | // While(true) { |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 96 | // Yield(next); |
| 97 | // auto tmp = current + next; |
| 98 | // current = next; |
| 99 | // next = tmp; |
| 100 | // } |
| 101 | // } |
| 102 | // |
| 103 | // // Start the execution of the coroutine. |
| 104 | // auto s = coroutine(); |
| 105 | // |
| 106 | // // Grab the first 20 yielded values and print them. |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 107 | // for(int i = 0; i < 20; i++) |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 108 | // { |
| 109 | // int val = 0; |
| 110 | // s->await(val); |
| 111 | // printf("Fibonacci(%d): %d", i, val); |
| 112 | // } |
| 113 | // |
| 114 | template<typename Return, typename... Arguments> |
| 115 | class Coroutine<Return(Arguments...)> |
| 116 | { |
| 117 | public: |
| 118 | Coroutine(); |
| 119 | |
| 120 | template<int index> |
| 121 | using CArgumentType = typename std::tuple_element<index, std::tuple<Arguments...>>::type; |
| 122 | |
| 123 | template<int index> |
| 124 | using RArgumentType = CToReactorT<CArgumentType<index>>; |
| 125 | |
| 126 | // Return the argument value with the given index. |
| 127 | template<int index> |
| 128 | Argument<RArgumentType<index>> Arg() const |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 129 | { |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 130 | Value *arg = Nucleus::getArgument(index); |
| 131 | return Argument<RArgumentType<index>>(arg); |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 132 | } |
| 133 | |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 134 | // Completes building of the coroutine and generates the coroutine's |
| 135 | // executable code. After calling, no more reactor functions may be |
| 136 | // called without building a new rr::Function or rr::Coroutine. |
| 137 | // While automatically called by operator(), finalize() should be called |
Ben Clayton | 20cf5c5 | 2019-07-01 11:13:27 +0100 | [diff] [blame] | 138 | // as soon as possible once the coroutine has been fully built. |
| 139 | // finalize() *must* be called explicitly on the same thread that |
| 140 | // instantiates the Coroutine instance if operator() is to be invoked on |
| 141 | // different threads. |
Nicolas Capens | 79d4c6c | 2022-04-22 17:20:26 -0400 | [diff] [blame] | 142 | inline void finalize(const char *name = "coroutine"); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 143 | |
| 144 | // Starts execution of the coroutine and returns a unique_ptr to a |
| 145 | // Stream<> that exposes the await() function for obtaining yielded |
| 146 | // values. |
| 147 | std::unique_ptr<Stream<Return>> operator()(Arguments...); |
| 148 | |
| 149 | protected: |
| 150 | std::unique_ptr<Nucleus> core; |
| 151 | std::shared_ptr<Routine> routine; |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 152 | std::vector<Type *> arguments; |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 153 | }; |
| 154 | |
| 155 | template<typename Return, typename... Arguments> |
| 156 | Coroutine<Return(Arguments...)>::Coroutine() |
Nicolas Capens | 3d26cfc | 2021-01-22 16:51:00 -0500 | [diff] [blame] | 157 | : core(new Nucleus()) |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 158 | { |
Nicolas Capens | 519cf22 | 2020-05-08 15:27:19 -0400 | [diff] [blame] | 159 | std::vector<Type *> types = { CToReactorT<Arguments>::type()... }; |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 160 | for(auto type : types) |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 161 | { |
Nicolas Capens | 519cf22 | 2020-05-08 15:27:19 -0400 | [diff] [blame] | 162 | if(type != Void::type()) |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 163 | { |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 164 | arguments.push_back(type); |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 165 | } |
| 166 | } |
| 167 | |
Nicolas Capens | 519cf22 | 2020-05-08 15:27:19 -0400 | [diff] [blame] | 168 | Nucleus::createCoroutine(CToReactorT<Return>::type(), arguments); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 169 | } |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 170 | |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 171 | template<typename Return, typename... Arguments> |
Nicolas Capens | 79d4c6c | 2022-04-22 17:20:26 -0400 | [diff] [blame] | 172 | void Coroutine<Return(Arguments...)>::finalize(const char *name /*= "coroutine"*/) |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 173 | { |
| 174 | if(core != nullptr) |
| 175 | { |
Nicolas Capens | 79d4c6c | 2022-04-22 17:20:26 -0400 | [diff] [blame] | 176 | routine = core->acquireCoroutine(name); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 177 | core.reset(nullptr); |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 178 | } |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | template<typename Return, typename... Arguments> |
| 182 | std::unique_ptr<Stream<Return>> |
| 183 | Coroutine<Return(Arguments...)>::operator()(Arguments... args) |
| 184 | { |
| 185 | finalize(); |
| 186 | |
Antonio Maiorano | 5ba2a5b | 2020-01-17 15:29:37 -0500 | [diff] [blame] | 187 | // TODO(b/148400732): Go back to just calling the CoroutineEntryBegin function directly. |
Devon Loehr | 3898549 | 2024-07-11 14:53:27 -0400 | [diff] [blame] | 188 | std::function<Nucleus::CoroutineHandle()> coroutineBegin = [this, args...] { |
Antonio Maiorano | 5ba2a5b | 2020-01-17 15:29:37 -0500 | [diff] [blame] | 189 | using Sig = Nucleus::CoroutineBegin<Arguments...>; |
| 190 | auto pfn = (Sig *)routine->getEntry(Nucleus::CoroutineEntryBegin); |
| 191 | auto handle = pfn(args...); |
| 192 | return handle; |
| 193 | }; |
| 194 | |
| 195 | auto handle = Nucleus::invokeCoroutineBegin(*routine, coroutineBegin); |
| 196 | |
Ben Clayton | 368d39c | 2020-01-08 23:10:47 +0000 | [diff] [blame] | 197 | return std::make_unique<Stream<Return>>(routine, handle); |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 198 | } |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 199 | |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 200 | # ifdef Yield // Defined in WinBase.h |
| 201 | # undef Yield |
| 202 | # endif |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 203 | |
Nicolas Capens | 157ba26 | 2019-12-10 17:49:14 -0500 | [diff] [blame] | 204 | // Suspends execution of the coroutine and yields val to the caller. |
| 205 | // Execution of the coroutine will resume after val is retrieved. |
| 206 | template<typename T> |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 207 | inline void Yield(const T &val) |
| 208 | { |
| 209 | Nucleus::yield(ValueOf(val)); |
| 210 | } |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 211 | |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 212 | } // namespace rr |
Ben Clayton | 1c82c7b | 2019-04-30 12:49:27 +0100 | [diff] [blame] | 213 | |
Ben Clayton | 713b8d3 | 2019-12-17 20:37:56 +0000 | [diff] [blame] | 214 | #endif // rr_ReactorCoroutine_hpp |