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