blob: 63f9408b60cc5b7fece2603df67c45169e090db6 [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
Ben Clayton713b8d32019-12-17 20:37:56 +000020# define rr_ReactorCoroutine_hpp
Ben Clayton1c82c7b2019-04-30 12:49:27 +010021
Nicolas Capens157ba262019-12-10 17:49:14 -050022namespace rr {
23
24// Base class for the template Stream<T>
25class StreamBase
Ben Clayton1c82c7b2019-04-30 12:49:27 +010026{
Nicolas Capens157ba262019-12-10 17:49:14 -050027protected:
28 StreamBase(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
Ben Clayton713b8d32019-12-17 20:37:56 +000029 : routine(routine)
30 , handle(handle)
31 {}
Nicolas Capens157ba262019-12-10 17:49:14 -050032
33 ~StreamBase()
Ben Clayton1c82c7b2019-04-30 12:49:27 +010034 {
Ben Clayton713b8d32019-12-17 20:37:56 +000035 auto pfn = (Nucleus::CoroutineDestroy *)routine->getEntry(Nucleus::CoroutineEntryDestroy);
Nicolas Capens157ba262019-12-10 17:49:14 -050036 pfn(handle);
37 }
Ben Clayton1c82c7b2019-04-30 12:49:27 +010038
Ben Clayton713b8d32019-12-17 20:37:56 +000039 bool await(void *out)
Nicolas Capens157ba262019-12-10 17:49:14 -050040 {
Ben Clayton713b8d32019-12-17 20:37:56 +000041 auto pfn = (Nucleus::CoroutineAwait *)routine->getEntry(Nucleus::CoroutineEntryAwait);
Nicolas Capens157ba262019-12-10 17:49:14 -050042 return pfn(handle, out);
43 }
Ben Clayton1c82c7b2019-04-30 12:49:27 +010044
45private:
Nicolas Capens157ba262019-12-10 17:49:14 -050046 std::shared_ptr<Routine> routine;
47 Nucleus::CoroutineHandle handle;
48};
Ben Clayton1c82c7b2019-04-30 12:49:27 +010049
Nicolas Capens157ba262019-12-10 17:49:14 -050050// 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().
53template<typename T>
54class Stream : public StreamBase
55{
56public:
57 inline Stream(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle)
Ben Clayton713b8d32019-12-17 20:37:56 +000058 : StreamBase(routine, handle)
59 {}
Nicolas Capens157ba262019-12-10 17:49:14 -050060
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.
Ben Clayton713b8d32019-12-17 20:37:56 +000065 inline bool await(T &out) { return StreamBase::await(&out); }
Nicolas Capens157ba262019-12-10 17:49:14 -050066};
67
68template<typename FunctionType>
69class 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;
Nicolas Capens81bc9d92019-12-16 15:05:57 -050095// While(true) {
Nicolas Capens157ba262019-12-10 17:49:14 -050096// 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.
Nicolas Capens81bc9d92019-12-16 15:05:57 -0500107// for(int i = 0; i < 20; i++)
Nicolas Capens157ba262019-12-10 17:49:14 -0500108// {
109// int val = 0;
110// s->await(val);
111// printf("Fibonacci(%d): %d", i, val);
112// }
113//
114template<typename Return, typename... Arguments>
115class Coroutine<Return(Arguments...)>
116{
117public:
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>
128 Argument<RArgumentType<index>> Arg() const
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100129 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500130 Value *arg = Nucleus::getArgument(index);
131 return Argument<RArgumentType<index>>(arg);
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100132 }
133
Nicolas Capens157ba262019-12-10 17:49:14 -0500134 // 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
Ben Clayton20cf5c52019-07-01 11:13:27 +0100138 // 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.
Nicolas Capens79d4c6c2022-04-22 17:20:26 -0400142 inline void finalize(const char *name = "coroutine");
Nicolas Capens157ba262019-12-10 17:49:14 -0500143
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
149protected:
150 std::unique_ptr<Nucleus> core;
151 std::shared_ptr<Routine> routine;
Ben Clayton713b8d32019-12-17 20:37:56 +0000152 std::vector<Type *> arguments;
Nicolas Capens157ba262019-12-10 17:49:14 -0500153};
154
155template<typename Return, typename... Arguments>
156Coroutine<Return(Arguments...)>::Coroutine()
Nicolas Capens3d26cfc2021-01-22 16:51:00 -0500157 : core(new Nucleus())
Nicolas Capens157ba262019-12-10 17:49:14 -0500158{
Nicolas Capens519cf222020-05-08 15:27:19 -0400159 std::vector<Type *> types = { CToReactorT<Arguments>::type()... };
Nicolas Capens157ba262019-12-10 17:49:14 -0500160 for(auto type : types)
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100161 {
Nicolas Capens519cf222020-05-08 15:27:19 -0400162 if(type != Void::type())
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100163 {
Nicolas Capens157ba262019-12-10 17:49:14 -0500164 arguments.push_back(type);
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100165 }
166 }
167
Nicolas Capens519cf222020-05-08 15:27:19 -0400168 Nucleus::createCoroutine(CToReactorT<Return>::type(), arguments);
Nicolas Capens157ba262019-12-10 17:49:14 -0500169}
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100170
Nicolas Capens157ba262019-12-10 17:49:14 -0500171template<typename Return, typename... Arguments>
Nicolas Capens79d4c6c2022-04-22 17:20:26 -0400172void Coroutine<Return(Arguments...)>::finalize(const char *name /*= "coroutine"*/)
Nicolas Capens157ba262019-12-10 17:49:14 -0500173{
174 if(core != nullptr)
175 {
Nicolas Capens79d4c6c2022-04-22 17:20:26 -0400176 routine = core->acquireCoroutine(name);
Nicolas Capens157ba262019-12-10 17:49:14 -0500177 core.reset(nullptr);
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100178 }
Nicolas Capens157ba262019-12-10 17:49:14 -0500179}
180
181template<typename Return, typename... Arguments>
182std::unique_ptr<Stream<Return>>
183Coroutine<Return(Arguments...)>::operator()(Arguments... args)
184{
185 finalize();
186
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500187 // TODO(b/148400732): Go back to just calling the CoroutineEntryBegin function directly.
Devon Loehr38985492024-07-11 14:53:27 -0400188 std::function<Nucleus::CoroutineHandle()> coroutineBegin = [this, args...] {
Antonio Maiorano5ba2a5b2020-01-17 15:29:37 -0500189 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
Ben Clayton368d39c2020-01-08 23:10:47 +0000197 return std::make_unique<Stream<Return>>(routine, handle);
Nicolas Capens157ba262019-12-10 17:49:14 -0500198}
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100199
Ben Clayton713b8d32019-12-17 20:37:56 +0000200# ifdef Yield // Defined in WinBase.h
201# undef Yield
202# endif
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100203
Nicolas Capens157ba262019-12-10 17:49:14 -0500204// Suspends execution of the coroutine and yields val to the caller.
205// Execution of the coroutine will resume after val is retrieved.
206template<typename T>
Ben Clayton713b8d32019-12-17 20:37:56 +0000207inline void Yield(const T &val)
208{
209 Nucleus::yield(ValueOf(val));
210}
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100211
Ben Clayton713b8d32019-12-17 20:37:56 +0000212} // namespace rr
Ben Clayton1c82c7b2019-04-30 12:49:27 +0100213
Ben Clayton713b8d32019-12-17 20:37:56 +0000214#endif // rr_ReactorCoroutine_hpp