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 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.
142 inline void finalize(const char *name = "coroutine", const Config::Edit &cfg = Config::Edit::None);
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;
152 std::vector<Type *> arguments;
153 };
154
155 template<typename Return, typename... Arguments>
Coroutine()156 Coroutine<Return(Arguments...)>::Coroutine()
157 : core(new Nucleus())
158 {
159 std::vector<Type *> types = { CToReactorT<Arguments>::type()... };
160 for(auto type : types)
161 {
162 if(type != Void::type())
163 {
164 arguments.push_back(type);
165 }
166 }
167
168 Nucleus::createCoroutine(CToReactorT<Return>::type(), arguments);
169 }
170
171 template<typename Return, typename... Arguments>
finalize(const char * name,const Config::Edit & cfg)172 void Coroutine<Return(Arguments...)>::finalize(const char *name /*= "coroutine"*/, const Config::Edit &cfg /* = Config::Edit::None */)
173 {
174 if(core != nullptr)
175 {
176 routine = core->acquireCoroutine(name, cfg);
177 core.reset(nullptr);
178 }
179 }
180
181 template<typename Return, typename... Arguments>
182 std::unique_ptr<Stream<Return>>
operator ()(Arguments...args)183 Coroutine<Return(Arguments...)>::operator()(Arguments... args)
184 {
185 finalize();
186
187 // TODO(b/148400732): Go back to just calling the CoroutineEntryBegin function directly.
188 std::function<Nucleus::CoroutineHandle()> coroutineBegin = [=] {
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
197 return std::make_unique<Stream<Return>>(routine, handle);
198 }
199
200 # ifdef Yield // Defined in WinBase.h
201 # undef Yield
202 # endif
203
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>
Yield(const T & val)207 inline void Yield(const T &val)
208 {
209 Nucleus::yield(ValueOf(val));
210 }
211
212 } // namespace rr
213
214 #endif // rr_ReactorCoroutine_hpp