2013-08-02 16:46:53 +02:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 CERN
|
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
2016-07-05 00:14:32 +02:00
|
|
|
* Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors.
|
2013-08-02 16:46:53 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __COROUTINE_H
|
|
|
|
#define __COROUTINE_H
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
2014-08-26 11:16:56 +02:00
|
|
|
#include <boost/version.hpp>
|
2016-07-05 00:14:32 +02:00
|
|
|
#include <type_traits>
|
2013-08-02 16:46:53 +02:00
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
#include <boost/context/fcontext.hpp>
|
|
|
|
#else
|
|
|
|
#include <boost/context/execution_context.hpp>
|
2016-07-05 18:02:50 +02:00
|
|
|
#include <boost/context/protected_fixedsize_stack.hpp>
|
2016-07-05 00:14:32 +02:00
|
|
|
#endif
|
2013-08-02 16:46:53 +02:00
|
|
|
|
|
|
|
/**
|
2013-10-14 16:13:35 +02:00
|
|
|
* Class COROUNTINE.
|
|
|
|
* Implements a coroutine. Wikipedia has a good explanation:
|
|
|
|
*
|
|
|
|
* "Coroutines are computer program components that generalize subroutines to
|
|
|
|
* allow multiple entry points for suspending and resuming execution at certain locations.
|
|
|
|
* Coroutines are well-suited for implementing more familiar program components such as cooperative
|
|
|
|
* tasks, exceptions, event loop, iterators, infinite lists and pipes."
|
|
|
|
*
|
|
|
|
* In other words, a coroutine can be considered a lightweight thread - which can be
|
|
|
|
* preempted only when it deliberately yields the control to the caller. This way,
|
|
|
|
* we avoid concurrency problems such as locking / race conditions.
|
|
|
|
*
|
|
|
|
* Uses boost::context library to do the actual context switching.
|
|
|
|
*
|
|
|
|
* This particular version takes a DELEGATE as an entry point, so it can invoke
|
|
|
|
* methods within a given object as separate coroutines.
|
|
|
|
*
|
|
|
|
* See coroutine_example.cpp for sample code.
|
2013-08-02 16:46:53 +02:00
|
|
|
*/
|
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
template <typename ReturnType, typename ArgType>
|
2013-08-08 14:59:59 +02:00
|
|
|
class COROUTINE
|
|
|
|
{
|
2013-08-02 16:46:53 +02:00
|
|
|
public:
|
2014-07-09 13:50:27 +02:00
|
|
|
COROUTINE() :
|
2016-07-05 00:14:32 +02:00
|
|
|
COROUTINE( nullptr )
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
* Creates a coroutine from a member method of an object
|
|
|
|
*/
|
2013-10-14 16:13:35 +02:00
|
|
|
template <class T>
|
|
|
|
COROUTINE( T* object, ReturnType(T::* ptr)( ArgType ) ) :
|
2016-07-05 00:14:32 +02:00
|
|
|
COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
* Creates a coroutine from a delegate object
|
|
|
|
*/
|
2016-07-05 00:14:32 +02:00
|
|
|
COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
|
|
|
|
m_func( std::move( aEntry ) ),
|
|
|
|
m_running( false ),
|
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
m_stack( nullptr ),
|
|
|
|
m_stackSize( c_defaultStackSize ),
|
|
|
|
#endif
|
|
|
|
m_caller( nullptr ),
|
|
|
|
m_callee( nullptr )
|
2014-07-09 13:50:27 +02:00
|
|
|
{
|
2015-04-03 11:13:06 +02:00
|
|
|
// Avoid not initialized members, and make static analysers quiet
|
|
|
|
m_args = 0;
|
|
|
|
m_retVal = 0;
|
2014-07-09 13:50:27 +02:00
|
|
|
}
|
2013-08-02 16:46:53 +02:00
|
|
|
|
2013-10-14 13:43:57 +02:00
|
|
|
~COROUTINE()
|
|
|
|
{
|
2014-08-26 11:16:56 +02:00
|
|
|
#if BOOST_VERSION >= 105600
|
2016-07-05 00:14:32 +02:00
|
|
|
delete m_callee;
|
2014-08-26 11:16:56 +02:00
|
|
|
#endif
|
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
delete m_caller;
|
|
|
|
|
2013-08-08 14:59:59 +02:00
|
|
|
if( m_stack )
|
|
|
|
free( m_stack );
|
2016-07-05 00:14:32 +02:00
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
private:
|
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
using context_type = boost::context::fcontext_t;
|
|
|
|
#else
|
|
|
|
using context_type = boost::context::execution_context<COROUTINE*>;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
public:
|
2013-10-14 13:43:57 +02:00
|
|
|
/**
|
|
|
|
* Function Yield()
|
|
|
|
*
|
|
|
|
* Stops execution of the coroutine and returns control to the caller.
|
|
|
|
* After a yield, Call() or Resume() methods invoked by the caller will
|
|
|
|
* immediately return true, indicating that we are not done yet, just asleep.
|
|
|
|
*/
|
|
|
|
void Yield()
|
|
|
|
{
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
jump( m_callee, m_caller, false );
|
|
|
|
#else
|
|
|
|
auto result = (*m_caller)( this );
|
|
|
|
*m_caller = std::move( std::get<0>( result ) );
|
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function Yield()
|
|
|
|
*
|
|
|
|
* Yield with a value - passes a value of given type to the caller.
|
|
|
|
* Useful for implementing generator objects.
|
|
|
|
*/
|
2013-10-14 20:40:36 +02:00
|
|
|
void Yield( ReturnType& aRetVal )
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
2013-10-14 20:40:36 +02:00
|
|
|
m_retVal = aRetVal;
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
jump( m_callee, m_caller, false );
|
|
|
|
#else
|
|
|
|
m_caller( this );
|
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-07-09 11:59:24 +02:00
|
|
|
* Function SetEntry()
|
2013-10-14 13:43:57 +02:00
|
|
|
*
|
|
|
|
* Defines the entry point for the coroutine, if not set in the constructor.
|
|
|
|
*/
|
2016-07-05 00:14:32 +02:00
|
|
|
void SetEntry( std::function<ReturnType(ArgType)> aEntry )
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
2016-07-05 00:14:32 +02:00
|
|
|
m_func = std::move( aEntry );
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Function Call()
|
|
|
|
*
|
|
|
|
* Starts execution of a coroutine, passing args as its arguments.
|
|
|
|
* @return true, if the coroutine has yielded and false if it has finished its
|
|
|
|
* execution (returned).
|
|
|
|
*/
|
2013-10-14 20:40:36 +02:00
|
|
|
bool Call( ArgType aArgs )
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
2016-07-05 00:14:32 +02:00
|
|
|
assert( m_callee == NULL );
|
|
|
|
assert( m_caller == NULL );
|
|
|
|
|
|
|
|
#if BOOST_VERSION <= 106000
|
2013-10-14 13:43:57 +02:00
|
|
|
// fixme: Clean up stack stuff. Add a guard
|
2013-08-08 14:59:59 +02:00
|
|
|
m_stack = malloc( c_defaultStackSize );
|
|
|
|
|
|
|
|
// align to 16 bytes
|
2013-10-14 16:13:35 +02:00
|
|
|
void* sp = (void*) ( ( ( (ptrdiff_t) m_stack ) + m_stackSize - 0xf ) & ( ~0x0f ) );
|
2013-08-08 14:59:59 +02:00
|
|
|
|
2014-07-09 13:50:27 +02:00
|
|
|
// correct the stack size
|
|
|
|
m_stackSize -= ( (size_t) m_stack + m_stackSize - (size_t) sp );
|
2016-07-05 00:14:32 +02:00
|
|
|
#endif
|
2014-07-09 13:50:27 +02:00
|
|
|
|
2013-10-14 20:40:36 +02:00
|
|
|
m_args = &aArgs;
|
2016-07-05 00:14:32 +02:00
|
|
|
|
|
|
|
#if BOOST_VERSION < 105600
|
|
|
|
m_callee = boost::context::make_fcontext( sp, m_stackSize, callerStub );
|
|
|
|
#elif BOOST_VERSION <= 106000
|
|
|
|
m_callee = new context_type( boost::context::make_fcontext( sp, m_stackSize, callerStub ) );
|
2014-08-26 11:16:56 +02:00
|
|
|
#else
|
2016-07-05 00:14:32 +02:00
|
|
|
m_callee = new context_type( std::allocator_arg_t(),
|
2016-07-05 18:02:50 +02:00
|
|
|
boost::context::protected_fixedsize_stack( c_defaultStackSize ), &COROUTINE::callerStub );
|
2016-07-05 00:14:32 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
m_caller = new context_type();
|
2014-08-26 11:16:56 +02:00
|
|
|
#endif
|
2013-08-08 14:59:59 +02:00
|
|
|
|
|
|
|
m_running = true;
|
2016-07-05 00:14:32 +02:00
|
|
|
|
2013-08-08 14:59:59 +02:00
|
|
|
// off we go!
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
jump( m_caller, m_callee, reinterpret_cast<intptr_t>( this ) );
|
|
|
|
#else
|
|
|
|
auto result = (*m_callee)( this );
|
|
|
|
*m_callee = std::move( std::get<0>( result ) );
|
|
|
|
#endif
|
2013-08-08 14:59:59 +02:00
|
|
|
return m_running;
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function Resume()
|
|
|
|
*
|
|
|
|
* Resumes execution of a previously yielded coroutine.
|
|
|
|
* @return true, if the coroutine has yielded again and false if it has finished its
|
|
|
|
* execution (returned).
|
|
|
|
*/
|
|
|
|
bool Resume()
|
|
|
|
{
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
jump( m_caller, m_callee, false );
|
|
|
|
#else
|
|
|
|
auto result = (*m_callee)( this );
|
|
|
|
*m_callee = std::move( std::get<0>( result ) );
|
|
|
|
#endif
|
2013-10-14 16:13:35 +02:00
|
|
|
|
2013-08-08 14:59:59 +02:00
|
|
|
return m_running;
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function ReturnValue()
|
|
|
|
*
|
|
|
|
* Returns the yielded value (the argument Yield() was called with)
|
|
|
|
*/
|
|
|
|
const ReturnType& ReturnValue() const
|
|
|
|
{
|
|
|
|
return m_retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function Running()
|
|
|
|
*
|
|
|
|
* @return true, if the coroutine is active
|
|
|
|
*/
|
|
|
|
bool Running() const
|
|
|
|
{
|
|
|
|
return m_running;
|
|
|
|
}
|
2013-08-02 16:46:53 +02:00
|
|
|
|
|
|
|
private:
|
2013-10-14 16:13:35 +02:00
|
|
|
static const int c_defaultStackSize = 2000000; // fixme: make configurable
|
2013-10-14 13:43:57 +02:00
|
|
|
|
|
|
|
/* real entry point of the coroutine */
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
2013-10-14 20:40:36 +02:00
|
|
|
static void callerStub( intptr_t aData )
|
2016-07-05 00:14:32 +02:00
|
|
|
#else
|
|
|
|
static context_type callerStub( context_type caller, COROUTINE* cor )
|
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
{
|
|
|
|
// get pointer to self
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
2013-10-14 20:40:36 +02:00
|
|
|
COROUTINE<ReturnType, ArgType>* cor = reinterpret_cast<COROUTINE<ReturnType, ArgType>*>( aData );
|
2016-07-05 00:14:32 +02:00
|
|
|
#else
|
|
|
|
cor->m_caller = &caller;
|
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
|
|
|
|
// call the coroutine method
|
2016-07-05 00:14:32 +02:00
|
|
|
cor->m_retVal = cor->m_func( *( cor->m_args ) );
|
2013-10-14 13:43:57 +02:00
|
|
|
cor->m_running = false;
|
|
|
|
|
|
|
|
// go back to wherever we came from.
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
jump( cor->m_callee, cor->m_caller, 0 );
|
|
|
|
#else
|
|
|
|
return caller;
|
|
|
|
#endif
|
2014-08-26 11:16:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///> Wrapper for jump_fcontext to assure compatibility between different boost versions
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
static inline intptr_t jump( context_type* aOld, context_type* aNew,
|
2014-08-26 11:16:56 +02:00
|
|
|
intptr_t aP, bool aPreserveFPU = true )
|
|
|
|
{
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION < 105600
|
2014-08-26 11:16:56 +02:00
|
|
|
return boost::context::jump_fcontext( aOld, aNew, aP, aPreserveFPU );
|
2016-07-05 00:14:32 +02:00
|
|
|
#else
|
|
|
|
return boost::context::jump_fcontext( aOld, *aNew, aP, aPreserveFPU );
|
2014-08-26 11:16:56 +02:00
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
}
|
2016-07-05 00:14:32 +02:00
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
std::function<ReturnType(ArgType)> m_func;
|
2013-10-14 13:43:57 +02:00
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
bool m_running;
|
2013-10-14 13:43:57 +02:00
|
|
|
|
2016-07-05 00:14:32 +02:00
|
|
|
#if BOOST_VERSION <= 106000
|
|
|
|
///< coroutine stack
|
|
|
|
void* m_stack;
|
|
|
|
|
|
|
|
size_t m_stackSize;
|
|
|
|
#endif
|
2013-10-14 13:43:57 +02:00
|
|
|
|
|
|
|
///< pointer to coroutine entry arguments. Stripped of references
|
|
|
|
///< to avoid compiler errors.
|
2016-07-05 00:14:32 +02:00
|
|
|
typename std::remove_reference<ArgType>::type* m_args;
|
|
|
|
|
2013-10-14 13:43:57 +02:00
|
|
|
ReturnType m_retVal;
|
|
|
|
|
|
|
|
///< saved caller context
|
2016-07-05 00:14:32 +02:00
|
|
|
context_type* m_caller;
|
2013-10-14 13:43:57 +02:00
|
|
|
|
|
|
|
///< saved coroutine context
|
2016-07-05 00:14:32 +02:00
|
|
|
context_type* m_callee;
|
2013-08-02 16:46:53 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|