Refactor coroutine to improve readability and removed unnecessary heap allocations. Added documentation/links to various boost doc revisions.
This commit is contained in:
parent
532634abf2
commit
18b7dbf4d1
|
@ -31,13 +31,51 @@
|
|||
#include <boost/version.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#if BOOST_VERSION <= 106000
|
||||
#if BOOST_VERSION < 106100
|
||||
#include <boost/context/fcontext.hpp>
|
||||
#else
|
||||
#include <boost/context/execution_context.hpp>
|
||||
#include <boost/context/protected_fixedsize_stack.hpp>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Note: in the history of boost, two changes to the context interface happened.
|
||||
* [1.54, 1.56)
|
||||
* http://www.boost.org/doc/libs/1_55_0/libs/context/doc/html/context/context/boost_fcontext.html
|
||||
* intptr_t jump_fcontext(
|
||||
* fcontext_t* ofc,
|
||||
* fcontext_t const* nfc,
|
||||
* intptr_t vp,
|
||||
* bool preserve_fpu = true
|
||||
* );
|
||||
*
|
||||
* fcontext_t* make_fcontext(
|
||||
* void* sp,
|
||||
* std::size_t size,
|
||||
* void (*fn)(intptr_t)
|
||||
* );
|
||||
*
|
||||
* [1.56, 1.61)
|
||||
* http://www.boost.org/doc/libs/1_56_0/libs/context/doc/html/context/context/boost_fcontext.html
|
||||
* intptr_t jump_fcontext(
|
||||
* fcontext_t* ofc,
|
||||
* fcontext_t nfc, <-----
|
||||
* intptr_t vp,
|
||||
* bool preserve_fpu = true
|
||||
* );
|
||||
*
|
||||
* fcontext_t make_fcontext( <-----
|
||||
* void* sp,
|
||||
* std::size_t size,
|
||||
* void(*fn)(intptr_t)
|
||||
* );
|
||||
*
|
||||
* [1.61, oo)
|
||||
* http://www.boost.org/doc/libs/1_61_0/libs/context/doc/html/context/ecv2.html
|
||||
* fcontext_t is hidden away behind the boost::execution_context(_v2) and the stack is created on behalf of
|
||||
* the user.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class COROUNTINE.
|
||||
* Implements a coroutine. Wikipedia has a good explanation:
|
||||
|
@ -73,7 +111,7 @@ public:
|
|||
* Creates a coroutine from a member method of an object
|
||||
*/
|
||||
template <class T>
|
||||
COROUTINE( T* object, ReturnType(T::* ptr)( ArgType ) ) :
|
||||
COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
|
||||
COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
|
||||
{
|
||||
}
|
||||
|
@ -85,34 +123,20 @@ public:
|
|||
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 ),
|
||||
m_args( 0 ),
|
||||
#if BOOST_VERSION < 106100 // -> m_callee = void* or void**
|
||||
m_callee( nullptr ),
|
||||
#endif
|
||||
m_caller( nullptr ),
|
||||
m_callee( nullptr )
|
||||
m_retVal( 0 )
|
||||
{
|
||||
// Avoid not initialized members, and make static analysers quiet
|
||||
m_args = 0;
|
||||
m_retVal = 0;
|
||||
}
|
||||
|
||||
~COROUTINE()
|
||||
{
|
||||
#if BOOST_VERSION >= 105600
|
||||
delete m_callee;
|
||||
#endif
|
||||
|
||||
#if BOOST_VERSION <= 106000
|
||||
delete m_caller;
|
||||
|
||||
if( m_stack )
|
||||
free( m_stack );
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if BOOST_VERSION <= 106000
|
||||
#if BOOST_VERSION < 106100
|
||||
using context_type = boost::context::fcontext_t;
|
||||
#else
|
||||
using context_type = boost::context::execution_context<COROUTINE*>;
|
||||
|
@ -128,12 +152,7 @@ public:
|
|||
*/
|
||||
void Yield()
|
||||
{
|
||||
#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
|
||||
jumpOut();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,11 +164,20 @@ public:
|
|||
void Yield( ReturnType& aRetVal )
|
||||
{
|
||||
m_retVal = aRetVal;
|
||||
#if BOOST_VERSION <= 106000
|
||||
jump( m_callee, m_caller, false );
|
||||
#else
|
||||
m_caller( this );
|
||||
#endif
|
||||
jumpOut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
jumpIn();
|
||||
return m_running;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,62 +198,37 @@ public:
|
|||
*/
|
||||
bool Call( ArgType aArgs )
|
||||
{
|
||||
assert( m_callee == NULL );
|
||||
assert( m_caller == NULL );
|
||||
|
||||
#if BOOST_VERSION <= 106000
|
||||
// fixme: Clean up stack stuff. Add a guard
|
||||
m_stack = malloc( c_defaultStackSize );
|
||||
|
||||
// align to 16 bytes
|
||||
void* sp = (void*) ( ( ( (ptrdiff_t) m_stack ) + m_stackSize - 0xf ) & ( ~0x0f ) );
|
||||
|
||||
// correct the stack size
|
||||
m_stackSize -= ( (size_t) m_stack + m_stackSize - (size_t) sp );
|
||||
#endif
|
||||
assert( m_func );
|
||||
assert( !m_callee );
|
||||
|
||||
m_args = &aArgs;
|
||||
|
||||
#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 ) );
|
||||
#else
|
||||
m_callee = new context_type( std::allocator_arg_t(),
|
||||
boost::context::protected_fixedsize_stack( c_defaultStackSize ), &COROUTINE::callerStub );
|
||||
#endif
|
||||
#if BOOST_VERSION < 106100
|
||||
assert( m_stack == nullptr );
|
||||
|
||||
#if BOOST_VERSION <= 106000
|
||||
m_caller = new context_type();
|
||||
// fixme: Clean up stack stuff. Add a guard
|
||||
size_t stackSize = c_defaultStackSize;
|
||||
m_stack.reset( new char[stackSize] );
|
||||
|
||||
// align to 16 bytes
|
||||
void* sp = (void*) ( ( ( (ptrdiff_t) m_stack.get() ) + stackSize - 0xf ) & ( ~0x0f ) );
|
||||
|
||||
// correct the stack size
|
||||
stackSize -= size_t( ( (ptrdiff_t) m_stack.get() + stackSize) - (ptrdiff_t) sp );
|
||||
|
||||
m_callee = boost::context::make_fcontext( sp, stackSize, callerStub );
|
||||
#else
|
||||
m_callee = context_type(
|
||||
std::allocator_arg_t(),
|
||||
boost::context::protected_fixedsize_stack( c_defaultStackSize ),
|
||||
&COROUTINE::callerStub
|
||||
);
|
||||
#endif
|
||||
|
||||
m_running = true;
|
||||
|
||||
// off we go!
|
||||
#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
|
||||
return m_running;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
#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
|
||||
jumpIn();
|
||||
|
||||
return m_running;
|
||||
}
|
||||
|
@ -254,66 +257,82 @@ private:
|
|||
static const int c_defaultStackSize = 2000000; // fixme: make configurable
|
||||
|
||||
/* real entry point of the coroutine */
|
||||
#if BOOST_VERSION <= 106000
|
||||
#if BOOST_VERSION < 106100
|
||||
static void callerStub( intptr_t aData )
|
||||
#else
|
||||
static context_type callerStub( context_type caller, COROUTINE* cor )
|
||||
#endif
|
||||
{
|
||||
// get pointer to self
|
||||
#if BOOST_VERSION <= 106000
|
||||
COROUTINE<ReturnType, ArgType>* cor = reinterpret_cast<COROUTINE<ReturnType, ArgType>*>( aData );
|
||||
#else
|
||||
cor->m_caller = &caller;
|
||||
#endif
|
||||
COROUTINE* cor = reinterpret_cast<COROUTINE*>( aData );
|
||||
|
||||
// call the coroutine method
|
||||
cor->m_retVal = cor->m_func( *( cor->m_args ) );
|
||||
cor->m_retVal = cor->m_func( *(cor->m_args) );
|
||||
cor->m_running = false;
|
||||
|
||||
// go back to wherever we came from.
|
||||
#if BOOST_VERSION <= 106000
|
||||
jump( cor->m_callee, cor->m_caller, 0 );
|
||||
cor->jumpOut();
|
||||
}
|
||||
#else
|
||||
return caller;
|
||||
/* real entry point of the coroutine */
|
||||
static context_type callerStub( context_type caller, COROUTINE* cor )
|
||||
{
|
||||
cor->m_caller = std::move( caller );
|
||||
|
||||
// call the coroutine method
|
||||
cor->m_retVal = cor->m_func( *(cor->m_args) );
|
||||
cor->m_running = false;
|
||||
|
||||
// go back to wherever we came from.
|
||||
return std::move( cor->m_caller );
|
||||
}
|
||||
#endif
|
||||
|
||||
void jumpIn()
|
||||
{
|
||||
#if BOOST_VERSION < 105600
|
||||
boost::context::jump_fcontext( &m_caller, m_callee, reinterpret_cast<intptr_t>(this) );
|
||||
#elif BOOST_VERSION < 106100
|
||||
boost::context::jump_fcontext( &m_caller, m_callee, reinterpret_cast<intptr_t>(this) );
|
||||
#else
|
||||
auto result = m_callee( this );
|
||||
m_callee = std::move( std::get<0>( result ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
///> Wrapper for jump_fcontext to assure compatibility between different boost versions
|
||||
#if BOOST_VERSION <= 106000
|
||||
static inline intptr_t jump( context_type* aOld, context_type* aNew,
|
||||
intptr_t aP, bool aPreserveFPU = true )
|
||||
void jumpOut()
|
||||
{
|
||||
#if BOOST_VERSION < 105600
|
||||
return boost::context::jump_fcontext( aOld, aNew, aP, aPreserveFPU );
|
||||
boost::context::jump_fcontext( m_callee, &m_caller, 0 );
|
||||
#elif BOOST_VERSION < 106100
|
||||
boost::context::jump_fcontext( &m_callee, m_caller, 0 );
|
||||
#else
|
||||
return boost::context::jump_fcontext( aOld, *aNew, aP, aPreserveFPU );
|
||||
auto result = m_caller( nullptr );
|
||||
m_caller = std::move( std::get<0>( result ) );
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
std::function<ReturnType(ArgType)> m_func;
|
||||
|
||||
bool m_running;
|
||||
|
||||
#if BOOST_VERSION <= 106000
|
||||
#if BOOST_VERSION < 106100
|
||||
///< coroutine stack
|
||||
void* m_stack;
|
||||
|
||||
size_t m_stackSize;
|
||||
std::unique_ptr<char[]> m_stack;
|
||||
#endif
|
||||
|
||||
///< pointer to coroutine entry arguments. Stripped of references
|
||||
///< to avoid compiler errors.
|
||||
typename std::remove_reference<ArgType>::type* m_args;
|
||||
|
||||
ReturnType m_retVal;
|
||||
|
||||
///< saved caller context
|
||||
context_type* m_caller;
|
||||
context_type m_caller;
|
||||
|
||||
///< saved coroutine context
|
||||
#if BOOST_VERSION < 105600
|
||||
context_type* m_callee;
|
||||
#else
|
||||
context_type m_callee;
|
||||
#endif
|
||||
|
||||
ReturnType m_retVal;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* 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>
|
||||
*
|
||||
* 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 __DELEGATE_H
|
||||
#define __DELEGATE_H
|
||||
|
||||
|
||||
/**
|
||||
* class DELEGATE
|
||||
* A trivial delegate (pointer to member method of an object) pattern implementation.
|
||||
* Check delegate_example.cpp for a coding sample.
|
||||
*/
|
||||
|
||||
template <class ReturnType, class Arg>
|
||||
class DELEGATE
|
||||
{
|
||||
public:
|
||||
typedef ReturnType (DELEGATE<ReturnType, Arg>::* MemberPointer)( Arg );
|
||||
typedef ReturnType _ReturnType;
|
||||
typedef Arg _ArgType;
|
||||
|
||||
DELEGATE()
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DELEGATE( T* aObject, ReturnType(T::* aPtr)( Arg ) )
|
||||
{
|
||||
m_ptr = reinterpret_cast<MemberPointer>( aPtr );
|
||||
m_object = reinterpret_cast<void*>( aObject );
|
||||
};
|
||||
|
||||
|
||||
ReturnType operator()( Arg aA ) const
|
||||
{
|
||||
DELEGATE<ReturnType, Arg>* casted = reinterpret_cast<DELEGATE<ReturnType, Arg>*>( m_object );
|
||||
return (casted->*m_ptr)( aA );
|
||||
}
|
||||
|
||||
private:
|
||||
MemberPointer m_ptr;
|
||||
void* m_object;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class DELEGATE0
|
||||
* Same as DELEGATE, but with no arguments.
|
||||
*/
|
||||
template <class ReturnType>
|
||||
class DELEGATE0
|
||||
{
|
||||
public:
|
||||
typedef ReturnType ( DELEGATE0<ReturnType>::* MemberPointer )();
|
||||
typedef ReturnType _ReturnType;
|
||||
|
||||
DELEGATE0()
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DELEGATE0( T* aObject, ReturnType(T::* aPtr)() )
|
||||
{
|
||||
m_ptr = reinterpret_cast<MemberPointer>( aPtr );
|
||||
m_object = reinterpret_cast<void*>( aObject );
|
||||
};
|
||||
|
||||
|
||||
ReturnType operator()() const
|
||||
{
|
||||
DELEGATE0<ReturnType>* casted = reinterpret_cast<DELEGATE0<ReturnType>*>( m_object );
|
||||
return ( casted->*m_ptr )();
|
||||
}
|
||||
|
||||
private:
|
||||
MemberPointer m_ptr;
|
||||
void* m_object;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue