coroutine.hh revision 14067
112800Sgiacomo.travaglini@arm.com/* 212800Sgiacomo.travaglini@arm.com * Copyright (c) 2018 ARM Limited 312800Sgiacomo.travaglini@arm.com * All rights reserved 412800Sgiacomo.travaglini@arm.com * 512800Sgiacomo.travaglini@arm.com * The license below extends only to copyright in the software and shall 612800Sgiacomo.travaglini@arm.com * not be construed as granting a license to any other intellectual 712800Sgiacomo.travaglini@arm.com * property including but not limited to intellectual property relating 812800Sgiacomo.travaglini@arm.com * to a hardware implementation of the functionality of the software 912800Sgiacomo.travaglini@arm.com * licensed hereunder. You may use the software subject to the license 1012800Sgiacomo.travaglini@arm.com * terms below provided that you ensure that this notice is replicated 1112800Sgiacomo.travaglini@arm.com * unmodified and in its entirety in all distributions of the software, 1212800Sgiacomo.travaglini@arm.com * modified or unmodified, in source code or in binary form. 1312800Sgiacomo.travaglini@arm.com * 1412800Sgiacomo.travaglini@arm.com * Redistribution and use in source and binary forms, with or without 1512800Sgiacomo.travaglini@arm.com * modification, are permitted provided that the following conditions are 1612800Sgiacomo.travaglini@arm.com * met: redistributions of source code must retain the above copyright 1712800Sgiacomo.travaglini@arm.com * notice, this list of conditions and the following disclaimer; 1812800Sgiacomo.travaglini@arm.com * redistributions in binary form must reproduce the above copyright 1912800Sgiacomo.travaglini@arm.com * notice, this list of conditions and the following disclaimer in the 2012800Sgiacomo.travaglini@arm.com * documentation and/or other materials provided with the distribution; 2112800Sgiacomo.travaglini@arm.com * neither the name of the copyright holders nor the names of its 2212800Sgiacomo.travaglini@arm.com * contributors may be used to endorse or promote products derived from 2312800Sgiacomo.travaglini@arm.com * this software without specific prior written permission. 2412800Sgiacomo.travaglini@arm.com * 2512800Sgiacomo.travaglini@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2612800Sgiacomo.travaglini@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2712800Sgiacomo.travaglini@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2812800Sgiacomo.travaglini@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2912800Sgiacomo.travaglini@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3012800Sgiacomo.travaglini@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3112800Sgiacomo.travaglini@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3212800Sgiacomo.travaglini@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3312800Sgiacomo.travaglini@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3412800Sgiacomo.travaglini@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3512800Sgiacomo.travaglini@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3612800Sgiacomo.travaglini@arm.com * 3712800Sgiacomo.travaglini@arm.com * Authors: Giacomo Travaglini 3812800Sgiacomo.travaglini@arm.com */ 3912800Sgiacomo.travaglini@arm.com 4012800Sgiacomo.travaglini@arm.com#ifndef __BASE_COROUTINE_HH__ 4112800Sgiacomo.travaglini@arm.com#define __BASE_COROUTINE_HH__ 4212800Sgiacomo.travaglini@arm.com 4312800Sgiacomo.travaglini@arm.com#include <functional> 4412800Sgiacomo.travaglini@arm.com#include <stack> 4512800Sgiacomo.travaglini@arm.com 4612800Sgiacomo.travaglini@arm.com#include "base/fiber.hh" 4712800Sgiacomo.travaglini@arm.com 4812800Sgiacomo.travaglini@arm.comnamespace m5 4912800Sgiacomo.travaglini@arm.com{ 5012800Sgiacomo.travaglini@arm.com 5112800Sgiacomo.travaglini@arm.com/** 5212800Sgiacomo.travaglini@arm.com * This template defines a Coroutine wrapper type with a Boost-like 5312800Sgiacomo.travaglini@arm.com * interface. It is built on top of the gem5 fiber class. 5412800Sgiacomo.travaglini@arm.com * The two template parameters (Arg and Ret) are the coroutine 5512800Sgiacomo.travaglini@arm.com * argument and coroutine return types which are passed between 5612800Sgiacomo.travaglini@arm.com * the coroutine and the caller via operator() and get() method. 5712800Sgiacomo.travaglini@arm.com * This implementation doesn't support passing multiple values, 5812800Sgiacomo.travaglini@arm.com * so a tuple must be used in that scenario. 5912800Sgiacomo.travaglini@arm.com * 6012800Sgiacomo.travaglini@arm.com * Most methods are templatized since it is relevant to distinguish 6112800Sgiacomo.travaglini@arm.com * the cases where one or both of the template parameters are void 6212800Sgiacomo.travaglini@arm.com */ 6312800Sgiacomo.travaglini@arm.comtemplate <typename Arg, typename Ret> 6412800Sgiacomo.travaglini@arm.comclass Coroutine : public Fiber 6512800Sgiacomo.travaglini@arm.com{ 6612800Sgiacomo.travaglini@arm.com 6712800Sgiacomo.travaglini@arm.com // This empty struct type is meant to replace coroutine channels 6812800Sgiacomo.travaglini@arm.com // in case the channel should be void (Coroutine template parameters 6912800Sgiacomo.travaglini@arm.com // are void. (See following ArgChannel, RetChannel typedef) 7012800Sgiacomo.travaglini@arm.com struct Empty {}; 7112800Sgiacomo.travaglini@arm.com using ArgChannel = typename std::conditional< 7212800Sgiacomo.travaglini@arm.com std::is_same<Arg, void>::value, Empty, std::stack<Arg>>::type; 7312800Sgiacomo.travaglini@arm.com 7412800Sgiacomo.travaglini@arm.com using RetChannel = typename std::conditional< 7512800Sgiacomo.travaglini@arm.com std::is_same<Ret, void>::value, Empty, std::stack<Ret>>::type; 7612800Sgiacomo.travaglini@arm.com 7712800Sgiacomo.travaglini@arm.com public: 7812800Sgiacomo.travaglini@arm.com /** 7912800Sgiacomo.travaglini@arm.com * CallerType: 8012800Sgiacomo.travaglini@arm.com * A reference to an object of this class will be passed 8112800Sgiacomo.travaglini@arm.com * to the coroutine task. This is the way it is possible 8212800Sgiacomo.travaglini@arm.com * for the coroutine to interface (e.g. switch back) 8312800Sgiacomo.travaglini@arm.com * to the coroutine caller. 8412800Sgiacomo.travaglini@arm.com */ 8512800Sgiacomo.travaglini@arm.com class CallerType 8612800Sgiacomo.travaglini@arm.com { 8712800Sgiacomo.travaglini@arm.com friend class Coroutine; 8812800Sgiacomo.travaglini@arm.com protected: 8912800Sgiacomo.travaglini@arm.com CallerType(Coroutine& _coro) : coro(_coro), callerFiber(nullptr) {} 9012800Sgiacomo.travaglini@arm.com 9112800Sgiacomo.travaglini@arm.com public: 9212800Sgiacomo.travaglini@arm.com /** 9312800Sgiacomo.travaglini@arm.com * operator() is the way we can jump outside the coroutine 9412800Sgiacomo.travaglini@arm.com * and return a value to the caller. 9512800Sgiacomo.travaglini@arm.com * 9612800Sgiacomo.travaglini@arm.com * This method is generated only if the coroutine returns 9712800Sgiacomo.travaglini@arm.com * a value (Ret != void) 9812800Sgiacomo.travaglini@arm.com */ 9912800Sgiacomo.travaglini@arm.com template <typename T = Ret> 10012800Sgiacomo.travaglini@arm.com CallerType& 10112800Sgiacomo.travaglini@arm.com operator()(typename std::enable_if< 10212800Sgiacomo.travaglini@arm.com !std::is_same<T, void>::value, T>::type param) 10312800Sgiacomo.travaglini@arm.com { 10412800Sgiacomo.travaglini@arm.com retChannel.push(param); 10512800Sgiacomo.travaglini@arm.com callerFiber->run(); 10612800Sgiacomo.travaglini@arm.com return *this; 10712800Sgiacomo.travaglini@arm.com } 10812800Sgiacomo.travaglini@arm.com 10912800Sgiacomo.travaglini@arm.com /** 11012800Sgiacomo.travaglini@arm.com * operator() is the way we can jump outside the coroutine 11112800Sgiacomo.travaglini@arm.com * 11212800Sgiacomo.travaglini@arm.com * This method is generated only if the coroutine doesn't 11312800Sgiacomo.travaglini@arm.com * return a value (Ret = void) 11412800Sgiacomo.travaglini@arm.com */ 11512800Sgiacomo.travaglini@arm.com template <typename T = Ret> 11612800Sgiacomo.travaglini@arm.com typename std::enable_if<std::is_same<T, void>::value, 11712800Sgiacomo.travaglini@arm.com CallerType>::type& 11812800Sgiacomo.travaglini@arm.com operator()() 11912800Sgiacomo.travaglini@arm.com { 12012800Sgiacomo.travaglini@arm.com callerFiber->run(); 12112800Sgiacomo.travaglini@arm.com return *this; 12212800Sgiacomo.travaglini@arm.com } 12312800Sgiacomo.travaglini@arm.com 12412800Sgiacomo.travaglini@arm.com /** 12512800Sgiacomo.travaglini@arm.com * get() is the way we can extrapolate arguments from the 12612800Sgiacomo.travaglini@arm.com * coroutine caller. 12712800Sgiacomo.travaglini@arm.com * The coroutine blocks, waiting for the value, unless it is already 12812800Sgiacomo.travaglini@arm.com * available; otherwise caller execution is resumed, 12912800Sgiacomo.travaglini@arm.com * and coroutine won't execute until a value is pushed 13012800Sgiacomo.travaglini@arm.com * from the caller. 13112800Sgiacomo.travaglini@arm.com * 13212800Sgiacomo.travaglini@arm.com * @return arg coroutine argument 13312800Sgiacomo.travaglini@arm.com */ 13412800Sgiacomo.travaglini@arm.com template <typename T = Arg> 13512800Sgiacomo.travaglini@arm.com typename std::enable_if<!std::is_same<T, void>::value, T>::type 13612800Sgiacomo.travaglini@arm.com get() 13712800Sgiacomo.travaglini@arm.com { 13812800Sgiacomo.travaglini@arm.com auto& args_channel = coro.argsChannel; 13912800Sgiacomo.travaglini@arm.com while (args_channel.empty()) { 14012800Sgiacomo.travaglini@arm.com callerFiber->run(); 14112800Sgiacomo.travaglini@arm.com } 14212800Sgiacomo.travaglini@arm.com 14312800Sgiacomo.travaglini@arm.com auto ret = args_channel.top(); 14412800Sgiacomo.travaglini@arm.com args_channel.pop(); 14512800Sgiacomo.travaglini@arm.com return ret; 14612800Sgiacomo.travaglini@arm.com } 14712800Sgiacomo.travaglini@arm.com 14812800Sgiacomo.travaglini@arm.com private: 14912800Sgiacomo.travaglini@arm.com Coroutine& coro; 15012800Sgiacomo.travaglini@arm.com Fiber* callerFiber; 15112800Sgiacomo.travaglini@arm.com RetChannel retChannel; 15212800Sgiacomo.travaglini@arm.com }; 15312800Sgiacomo.travaglini@arm.com 15412800Sgiacomo.travaglini@arm.com Coroutine() = delete; 15512800Sgiacomo.travaglini@arm.com Coroutine(const Coroutine& rhs) = delete; 15612800Sgiacomo.travaglini@arm.com Coroutine& operator=(const Coroutine& rhs) = delete; 15712800Sgiacomo.travaglini@arm.com 15812800Sgiacomo.travaglini@arm.com /** 15912800Sgiacomo.travaglini@arm.com * Coroutine constructor. 16012800Sgiacomo.travaglini@arm.com * The only way to construct a coroutine is to pass it the routine 16112800Sgiacomo.travaglini@arm.com * it needs to run. The first argument of the function should be a 16212800Sgiacomo.travaglini@arm.com * reference to the Coroutine<Arg,Ret>::caller_type which the 16312800Sgiacomo.travaglini@arm.com * routine will use as a way for yielding to the caller. 16414067SMichiel.VanTol@arm.com * The optional second boolean argument controls if the Coroutine 16514067SMichiel.VanTol@arm.com * should be run on creation, which mimics Boost's Coroutine 16614067SMichiel.VanTol@arm.com * semantics by default. This can be disabled as an optimization to 16714067SMichiel.VanTol@arm.com * avoid unnecessary context switches on Coroutine creation. 16812800Sgiacomo.travaglini@arm.com * 16912800Sgiacomo.travaglini@arm.com * @param f task run by the coroutine 17014067SMichiel.VanTol@arm.com * @param run_coroutine set to false to disable running the coroutine 17114067SMichiel.VanTol@arm.com * immediately after it is created 17212800Sgiacomo.travaglini@arm.com */ 17314067SMichiel.VanTol@arm.com Coroutine(std::function<void(CallerType&)> f, bool run_coroutine = true) 17412800Sgiacomo.travaglini@arm.com : Fiber(), task(f), caller(*this) 17512800Sgiacomo.travaglini@arm.com { 17614067SMichiel.VanTol@arm.com // When desired, run the Coroutine after it is created 17714067SMichiel.VanTol@arm.com if (run_coroutine) 17814067SMichiel.VanTol@arm.com this->call(); 17912800Sgiacomo.travaglini@arm.com } 18012800Sgiacomo.travaglini@arm.com 18112800Sgiacomo.travaglini@arm.com virtual ~Coroutine() {} 18212800Sgiacomo.travaglini@arm.com 18312800Sgiacomo.travaglini@arm.com public: 18412800Sgiacomo.travaglini@arm.com /** Coroutine interface */ 18512800Sgiacomo.travaglini@arm.com 18612800Sgiacomo.travaglini@arm.com /** 18712800Sgiacomo.travaglini@arm.com * operator() is the way we can jump inside the coroutine 18812800Sgiacomo.travaglini@arm.com * and passing arguments. 18912800Sgiacomo.travaglini@arm.com * 19012800Sgiacomo.travaglini@arm.com * This method is generated only if the coroutine takes 19112800Sgiacomo.travaglini@arm.com * arguments (Arg != void) 19212800Sgiacomo.travaglini@arm.com */ 19312800Sgiacomo.travaglini@arm.com template <typename T = Arg> 19412800Sgiacomo.travaglini@arm.com Coroutine& 19512800Sgiacomo.travaglini@arm.com operator()(typename std::enable_if< 19612800Sgiacomo.travaglini@arm.com !std::is_same<T, void>::value, T>::type param) 19712800Sgiacomo.travaglini@arm.com { 19812800Sgiacomo.travaglini@arm.com argsChannel.push(param); 19912800Sgiacomo.travaglini@arm.com this->call(); 20012800Sgiacomo.travaglini@arm.com return *this; 20112800Sgiacomo.travaglini@arm.com } 20212800Sgiacomo.travaglini@arm.com 20312800Sgiacomo.travaglini@arm.com /** 20412800Sgiacomo.travaglini@arm.com * operator() is the way we can jump inside the coroutine. 20512800Sgiacomo.travaglini@arm.com * 20612800Sgiacomo.travaglini@arm.com * This method is generated only if the coroutine takes 20712800Sgiacomo.travaglini@arm.com * no arguments. (Arg = void) 20812800Sgiacomo.travaglini@arm.com */ 20912800Sgiacomo.travaglini@arm.com template <typename T = Arg> 21012800Sgiacomo.travaglini@arm.com typename std::enable_if<std::is_same<T, void>::value, Coroutine>::type& 21112800Sgiacomo.travaglini@arm.com operator()() 21212800Sgiacomo.travaglini@arm.com { 21312800Sgiacomo.travaglini@arm.com this->call(); 21412800Sgiacomo.travaglini@arm.com return *this; 21512800Sgiacomo.travaglini@arm.com } 21612800Sgiacomo.travaglini@arm.com 21712800Sgiacomo.travaglini@arm.com /** 21812800Sgiacomo.travaglini@arm.com * get() is the way we can extrapolate return values 21912800Sgiacomo.travaglini@arm.com * (yielded) from the coroutine. 22012800Sgiacomo.travaglini@arm.com * The caller blocks, waiting for the value, unless it is already 22112800Sgiacomo.travaglini@arm.com * available; otherwise coroutine execution is resumed, 22212800Sgiacomo.travaglini@arm.com * and caller won't execute until a value is yielded back 22312800Sgiacomo.travaglini@arm.com * from the coroutine. 22412800Sgiacomo.travaglini@arm.com * 22512800Sgiacomo.travaglini@arm.com * @return ret yielded value 22612800Sgiacomo.travaglini@arm.com */ 22712800Sgiacomo.travaglini@arm.com template <typename T = Ret> 22812800Sgiacomo.travaglini@arm.com typename std::enable_if<!std::is_same<T, void>::value, T>::type 22912800Sgiacomo.travaglini@arm.com get() 23012800Sgiacomo.travaglini@arm.com { 23112800Sgiacomo.travaglini@arm.com auto& ret_channel = caller.retChannel; 23212800Sgiacomo.travaglini@arm.com while (ret_channel.empty()) { 23312800Sgiacomo.travaglini@arm.com this->call(); 23412800Sgiacomo.travaglini@arm.com } 23512800Sgiacomo.travaglini@arm.com 23612800Sgiacomo.travaglini@arm.com auto ret = ret_channel.top(); 23712800Sgiacomo.travaglini@arm.com ret_channel.pop(); 23812800Sgiacomo.travaglini@arm.com return ret; 23912800Sgiacomo.travaglini@arm.com } 24012800Sgiacomo.travaglini@arm.com 24112800Sgiacomo.travaglini@arm.com /** Check if coroutine is still running */ 24212800Sgiacomo.travaglini@arm.com operator bool() const { return !this->finished(); } 24312800Sgiacomo.travaglini@arm.com 24412800Sgiacomo.travaglini@arm.com private: 24512800Sgiacomo.travaglini@arm.com /** 24612800Sgiacomo.travaglini@arm.com * Overriding base (Fiber) main. 24712800Sgiacomo.travaglini@arm.com * This method will be automatically called by the Fiber 24812800Sgiacomo.travaglini@arm.com * running engine and it is a simple wrapper for the task 24912800Sgiacomo.travaglini@arm.com * that the coroutine is supposed to run. 25012800Sgiacomo.travaglini@arm.com */ 25112800Sgiacomo.travaglini@arm.com void main() override { this->task(caller); } 25212800Sgiacomo.travaglini@arm.com 25312800Sgiacomo.travaglini@arm.com void 25412800Sgiacomo.travaglini@arm.com call() 25512800Sgiacomo.travaglini@arm.com { 25612800Sgiacomo.travaglini@arm.com caller.callerFiber = currentFiber(); 25712800Sgiacomo.travaglini@arm.com run(); 25812800Sgiacomo.travaglini@arm.com } 25912800Sgiacomo.travaglini@arm.com 26012800Sgiacomo.travaglini@arm.com private: 26112800Sgiacomo.travaglini@arm.com /** Arguments for the coroutine */ 26212800Sgiacomo.travaglini@arm.com ArgChannel argsChannel; 26312800Sgiacomo.travaglini@arm.com 26412800Sgiacomo.travaglini@arm.com /** Coroutine task */ 26512800Sgiacomo.travaglini@arm.com std::function<void(CallerType&)> task; 26612800Sgiacomo.travaglini@arm.com 26712800Sgiacomo.travaglini@arm.com /** Coroutine caller */ 26812800Sgiacomo.travaglini@arm.com CallerType caller; 26912800Sgiacomo.travaglini@arm.com}; 27012800Sgiacomo.travaglini@arm.com 27112800Sgiacomo.travaglini@arm.com} //namespace m5 27212800Sgiacomo.travaglini@arm.com 27312800Sgiacomo.travaglini@arm.com#endif // __BASE_COROUTINE_HH__ 274