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
20 # define rr_ReactorCoroutine_hpp
21
22 namespace rr {
23
24 // Base class for the template Stream<T>
25 class StreamBase
26 {
27 protected:
StreamBase(const std::shared_ptr<Routine> & routine,Nucleus::CoroutineHandle handle)28 StreamBase(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
29 : routine(routine)
30 , handle(handle)
31 {}
32
~StreamBase()33 ~StreamBase()
34 {
35 auto pfn = (Nucleus::CoroutineDestroy *)routine->getEntry(Nucleus::CoroutineEntryDestroy);
36 pfn(handle);
37 }
38
await(void * out)39 bool await(void *out)
40 {
41 auto pfn = (Nucleus::CoroutineAwait *)routine->getEntry(Nucleus::CoroutineEntryAwait);
42 return pfn(handle, out);
43 }
44
45 private:
46 std::shared_ptr<Routine> routine;
47 Nucleus::CoroutineHandle handle;
48 };
49
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:
Stream(const std::shared_ptr<Routine> & routine,Nucleus::CoroutineHandle handle)57 inline Stream(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
58 : StreamBase(routine, handle)
59 {}
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.
await(T & out)65 inline bool await(T &out) { return StreamBase::await(&out); }
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;
95 // While(true) {
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.
107 // for(int i = 0; i < 20; i++)
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>
Arg() const128 Argument<RArgumentType<index>> Arg() const
129 {
130 Value *arg = Nucleus::getArgument(index);
131 return Argument<RArgumentType<index>>(arg);
132 }
133
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
138 // as early as possible to release the global Reactor mutex lock.
139 // It must also be called explicitly on the same thread that instantiates
140 // the Coroutine instance if operator() is invoked on separate threads.
141 // This is because presently, Reactor backends use a global mutex scoped
142 // to the generation of routines, and these must be locked/unlocked on the
143 // same thread.
144 inline void finalize(const Config::Edit &cfg = Config::Edit::None);
145
146 // Starts execution of the coroutine and returns a unique_ptr to a
147 // Stream<> that exposes the await() function for obtaining yielded
148 // values.
149 std::unique_ptr<Stream<Return>> operator()(Arguments...);
150
151 protected:
152 std::unique_ptr<Nucleus> core;
153 std::shared_ptr<Routine> routine;
154 std::vector<Type *> arguments;
155 };
156
157 template<typename Return, typename... Arguments>
Coroutine()158 Coroutine<Return(Arguments...)>::Coroutine()
159 {
160 core.reset(new Nucleus());
161
162 std::vector<Type *> types = { CToReactorT<Arguments>::getType()... };
163 for(auto type : types)
164 {
165 if(type != Void::getType())
166 {
167 arguments.push_back(type);
168 }
169 }
170
171 Nucleus::createCoroutine(CToReactorT<Return>::getType(), arguments);
172 }
173
174 template<typename Return, typename... Arguments>
finalize(const Config::Edit & cfg)175 void Coroutine<Return(Arguments...)>::finalize(const Config::Edit &cfg /* = Config::Edit::None */)
176 {
177 if(core != nullptr)
178 {
179 routine = core->acquireCoroutine("coroutine", cfg);
180 core.reset(nullptr);
181 }
182 }
183
184 template<typename Return, typename... Arguments>
185 std::unique_ptr<Stream<Return>>
operator ()(Arguments...args)186 Coroutine<Return(Arguments...)>::operator()(Arguments... args)
187 {
188 finalize();
189
190 // TODO(b/148400732): Go back to just calling the CoroutineEntryBegin function directly.
191 std::function<Nucleus::CoroutineHandle()> coroutineBegin = [=] {
192 using Sig = Nucleus::CoroutineBegin<Arguments...>;
193 auto pfn = (Sig *)routine->getEntry(Nucleus::CoroutineEntryBegin);
194 auto handle = pfn(args...);
195 return handle;
196 };
197
198 auto handle = Nucleus::invokeCoroutineBegin(*routine, coroutineBegin);
199
200 return std::make_unique<Stream<Return>>(routine, handle);
201 }
202
203 # ifdef Yield // Defined in WinBase.h
204 # undef Yield
205 # endif
206
207 // Suspends execution of the coroutine and yields val to the caller.
208 // Execution of the coroutine will resume after val is retrieved.
209 template<typename T>
Yield(const T & val)210 inline void Yield(const T &val)
211 {
212 Nucleus::yield(ValueOf(val));
213 }
214
215 } // namespace rr
216
217 #endif // rr_ReactorCoroutine_hpp