• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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