• 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 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