blob: cd4f81ce9e8ec4b7648a81aa5c3674e820081896 [file] [log] [blame]
Ben Clayton1c82c7b2019-04-30 12:49:27 +01001// 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
22namespace rr
23{
24 // Base class for the template Stream<T>
25 class StreamBase
26 {
27 protected:
28 StreamBase(std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
29 : routine(routine), handle(handle) {}
30
31 ~StreamBase()
32 {
33 auto pfn = (Nucleus::CoroutineDestroy*)routine->getEntry(Nucleus::CoroutineEntryDestroy);
34 pfn(handle);
35 }
36
37 bool await(void* out)
38 {
39 auto pfn = (Nucleus::CoroutineAwait*)routine->getEntry(Nucleus::CoroutineEntryAwait);
40 return pfn(handle, out);
41 }
42
43private:
44 std::shared_ptr<Routine> routine;
45 Nucleus::CoroutineHandle handle;
46 };
47
48 // Stream is the interface to a running Coroutine instance.
49 // A Coroutine may Yield() values of type T, which can be retrieved with
50 // await().
51 template<typename T>
52 class Stream : public StreamBase
53 {
54 public:
55 inline Stream(std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
56 : StreamBase(routine, handle) {}
57
58 // await() retrieves the next yielded value from the coroutine.
59 // Returns true if the coroutine yieled a value and out was assigned a
60 // new value. If await() returns false, the coroutine has finished
61 // execution and await() will return false for all future calls.
62 inline bool await(T& out) { return StreamBase::await(&out); }
63 };
64
65 template<typename FunctionType>
66 class Coroutine;
67
68 // Coroutine constructs a reactor Coroutine function.
69 // rr::Coroutine is similar to rr::Function in that it builds a new
70 // executable function, but Coroutines have the following differences:
71 // (1) Coroutines do not support Return() statements.
72 // (2) Coroutines support Yield() statements to suspend execution of the
73 // coroutine and pass a value up to the caller. Yield can be called
74 // multiple times in a single execution of a coroutine.
75 // (3) The template argument T to Coroutine<T> is a C-style function
76 // signature.
77 // (4) Coroutine::operator() returns a rr::Stream<T> instead of an
78 // rr::Routine.
79 // (5) operator() starts execution of the coroutine immediately.
80 // (6) operator() uses the Coroutine's template function signature to
81 // ensure the argument types match the generated function signature.
82 //
83 // Example usage:
84 //
85 // // Build the coroutine function
86 // Coroutine<int()> coroutine;
87 // {
88 // Yield(Int(0));
89 // Yield(Int(1));
90 // Int current = 1;
91 // Int next = 1;
92 // While (true) {
93 // Yield(next);
94 // auto tmp = current + next;
95 // current = next;
96 // next = tmp;
97 // }
98 // }
99 //
100 // // Start the execution of the coroutine.
101 // auto s = coroutine();
102 //
103 // // Grab the first 20 yielded values and print them.
104 // for (int i = 0; i < 20; i++)
105 // {
106 // int val = 0;
107 // s->await(val);
108 // printf("Fibonacci(%d): %d", i, val);
109 // }
110 //
111 template<typename Return, typename... Arguments>
112 class Coroutine<Return(Arguments...)>
113 {
114 public:
115 Coroutine();
116
117 template<int index>
118 using CArgumentType = typename std::tuple_element<index, std::tuple<Arguments...>>::type;
119
120 template<int index>
121 using RArgumentType = CToReactor<CArgumentType<index>>;
122
123 // Return the argument value with the given index.
124 template<int index>
125 Argument<RArgumentType<index>> Arg() const
126 {
127 Value *arg = Nucleus::getArgument(index);
128 return Argument<RArgumentType<index>>(arg);
129 }
130
131 // Completes building of the coroutine and generates the coroutine's
132 // executable code. After calling, no more reactor functions may be
133 // called without building a new rr::Function or rr::Coroutine.
134 // While automatically called by operator(), finalize() should be called
135 // as early as possible to release the global Reactor mutex lock.
136 inline void finalize();
137
138 // Starts execution of the coroutine and returns a unique_ptr to a
139 // Stream<> that exposes the await() function for obtaining yielded
140 // values.
141 std::unique_ptr<Stream<Return>> operator()(Arguments...);
142
143 protected:
144 std::unique_ptr<Nucleus> core;
145 std::shared_ptr<Routine> routine;
146 std::vector<Type*> arguments;
147 };
148
149 template<typename Return, typename... Arguments>
150 Coroutine<Return(Arguments...)>::Coroutine() : routine{}
151 {
152 core.reset(new Nucleus());
153
154 std::vector<Type*> types = {CToReactor<Arguments>::getType()...};
155 for(auto type : types)
156 {
157 if(type != Void::getType())
158 {
159 arguments.push_back(type);
160 }
161 }
162
163 Nucleus::createCoroutine(CToReactor<Return>::getType(), arguments);
164 }
165
166 template<typename Return, typename... Arguments>
167 void Coroutine<Return(Arguments...)>::finalize()
168 {
169 if(core != nullptr)
170 {
171 routine.reset(core->acquireCoroutine("coroutine", true));
172 core.reset(nullptr);
173 }
174 }
175
176 template<typename Return, typename... Arguments>
177 std::unique_ptr<Stream<Return>>
178 Coroutine<Return(Arguments...)>::operator()(Arguments... args)
179 {
180 finalize();
181
182 using Sig = Nucleus::CoroutineBegin<Arguments...>;
183 auto pfn = (Sig*)routine->getEntry(Nucleus::CoroutineEntryBegin);
184 auto handle = pfn(args...);
185 return std::unique_ptr<Stream<Return>>(new Stream<Return>(routine, handle));
186 }
187
188#ifdef Yield // Defined in WinBase.h
189#undef Yield
190#endif
191
192 // Suspends execution of the coroutine and yields val to the caller.
193 // Execution of the coroutine will resume after val is retrieved.
194 template<typename T>
195 inline void Yield(const T &val) { Nucleus::yield(ValueOf(val)); }
196
197} // namespace rr
198
199#endif // rr_ReactorCoroutine_hpp