| // Copyright 2019 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "Reactor.hpp" |
| |
| #include <memory> |
| |
| #ifndef rr_ReactorCoroutine_hpp |
| # define rr_ReactorCoroutine_hpp |
| |
| namespace rr { |
| |
| // Base class for the template Stream<T> |
| class StreamBase |
| { |
| protected: |
| StreamBase(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle) |
| : routine(routine) |
| , handle(handle) |
| {} |
| |
| ~StreamBase() |
| { |
| auto pfn = (Nucleus::CoroutineDestroy *)routine->getEntry(Nucleus::CoroutineEntryDestroy); |
| pfn(handle); |
| } |
| |
| bool await(void *out) |
| { |
| auto pfn = (Nucleus::CoroutineAwait *)routine->getEntry(Nucleus::CoroutineEntryAwait); |
| return pfn(handle, out); |
| } |
| |
| private: |
| std::shared_ptr<Routine> routine; |
| Nucleus::CoroutineHandle handle; |
| }; |
| |
| // Stream is the interface to a running Coroutine instance. |
| // A Coroutine may Yield() values of type T, which can be retrieved with |
| // await(). |
| template<typename T> |
| class Stream : public StreamBase |
| { |
| public: |
| inline Stream(const std::shared_ptr<Routine> &routine, Nucleus::CoroutineHandle handle) |
| : StreamBase(routine, handle) |
| {} |
| |
| // await() retrieves the next yielded value from the coroutine. |
| // Returns true if the coroutine yieled a value and out was assigned a |
| // new value. If await() returns false, the coroutine has finished |
| // execution and await() will return false for all future calls. |
| inline bool await(T &out) { return StreamBase::await(&out); } |
| }; |
| |
| template<typename FunctionType> |
| class Coroutine; |
| |
| // Coroutine constructs a reactor Coroutine function. |
| // rr::Coroutine is similar to rr::Function in that it builds a new |
| // executable function, but Coroutines have the following differences: |
| // (1) Coroutines do not support Return() statements. |
| // (2) Coroutines support Yield() statements to suspend execution of the |
| // coroutine and pass a value up to the caller. Yield can be called |
| // multiple times in a single execution of a coroutine. |
| // (3) The template argument T to Coroutine<T> is a C-style function |
| // signature. |
| // (4) Coroutine::operator() returns a rr::Stream<T> instead of an |
| // rr::Routine. |
| // (5) operator() starts execution of the coroutine immediately. |
| // (6) operator() uses the Coroutine's template function signature to |
| // ensure the argument types match the generated function signature. |
| // |
| // Example usage: |
| // |
| // // Build the coroutine function |
| // Coroutine<int()> coroutine; |
| // { |
| // Yield(Int(0)); |
| // Yield(Int(1)); |
| // Int current = 1; |
| // Int next = 1; |
| // While(true) { |
| // Yield(next); |
| // auto tmp = current + next; |
| // current = next; |
| // next = tmp; |
| // } |
| // } |
| // |
| // // Start the execution of the coroutine. |
| // auto s = coroutine(); |
| // |
| // // Grab the first 20 yielded values and print them. |
| // for(int i = 0; i < 20; i++) |
| // { |
| // int val = 0; |
| // s->await(val); |
| // printf("Fibonacci(%d): %d", i, val); |
| // } |
| // |
| template<typename Return, typename... Arguments> |
| class Coroutine<Return(Arguments...)> |
| { |
| public: |
| Coroutine(); |
| |
| template<int index> |
| using CArgumentType = typename std::tuple_element<index, std::tuple<Arguments...>>::type; |
| |
| template<int index> |
| using RArgumentType = CToReactorT<CArgumentType<index>>; |
| |
| // Return the argument value with the given index. |
| template<int index> |
| Argument<RArgumentType<index>> Arg() const |
| { |
| Value *arg = Nucleus::getArgument(index); |
| return Argument<RArgumentType<index>>(arg); |
| } |
| |
| // Completes building of the coroutine and generates the coroutine's |
| // executable code. After calling, no more reactor functions may be |
| // called without building a new rr::Function or rr::Coroutine. |
| // While automatically called by operator(), finalize() should be called |
| // as soon as possible once the coroutine has been fully built. |
| // finalize() *must* be called explicitly on the same thread that |
| // instantiates the Coroutine instance if operator() is to be invoked on |
| // different threads. |
| inline void finalize(const char *name = "coroutine"); |
| |
| // Starts execution of the coroutine and returns a unique_ptr to a |
| // Stream<> that exposes the await() function for obtaining yielded |
| // values. |
| std::unique_ptr<Stream<Return>> operator()(Arguments...); |
| |
| protected: |
| std::unique_ptr<Nucleus> core; |
| std::shared_ptr<Routine> routine; |
| std::vector<Type *> arguments; |
| }; |
| |
| template<typename Return, typename... Arguments> |
| Coroutine<Return(Arguments...)>::Coroutine() |
| : core(new Nucleus()) |
| { |
| std::vector<Type *> types = { CToReactorT<Arguments>::type()... }; |
| for(auto type : types) |
| { |
| if(type != Void::type()) |
| { |
| arguments.push_back(type); |
| } |
| } |
| |
| Nucleus::createCoroutine(CToReactorT<Return>::type(), arguments); |
| } |
| |
| template<typename Return, typename... Arguments> |
| void Coroutine<Return(Arguments...)>::finalize(const char *name /*= "coroutine"*/) |
| { |
| if(core != nullptr) |
| { |
| routine = core->acquireCoroutine(name); |
| core.reset(nullptr); |
| } |
| } |
| |
| template<typename Return, typename... Arguments> |
| std::unique_ptr<Stream<Return>> |
| Coroutine<Return(Arguments...)>::operator()(Arguments... args) |
| { |
| finalize(); |
| |
| // TODO(b/148400732): Go back to just calling the CoroutineEntryBegin function directly. |
| std::function<Nucleus::CoroutineHandle()> coroutineBegin = [this, args...] { |
| using Sig = Nucleus::CoroutineBegin<Arguments...>; |
| auto pfn = (Sig *)routine->getEntry(Nucleus::CoroutineEntryBegin); |
| auto handle = pfn(args...); |
| return handle; |
| }; |
| |
| auto handle = Nucleus::invokeCoroutineBegin(*routine, coroutineBegin); |
| |
| return std::make_unique<Stream<Return>>(routine, handle); |
| } |
| |
| # ifdef Yield // Defined in WinBase.h |
| # undef Yield |
| # endif |
| |
| // Suspends execution of the coroutine and yields val to the caller. |
| // Execution of the coroutine will resume after val is retrieved. |
| template<typename T> |
| inline void Yield(const T &val) |
| { |
| Nucleus::yield(ValueOf(val)); |
| } |
| |
| } // namespace rr |
| |
| #endif // rr_ReactorCoroutine_hpp |