KiCad PCB EDA Suite
COROUTINE< ReturnType, ArgType > Class Template Reference

Implement a coroutine. More...

#include <coroutine.h>

Classes

class  CALL_CONTEXT
 
struct  CONTEXT_T
 
struct  INVOCATION_ARGS
 

Public Member Functions

 COROUTINE ()
 
template<class T >
 COROUTINE (T *object, ReturnType(T::*ptr)(ArgType))
 Create a coroutine from a member method of an object. More...
 
 COROUTINE (std::function< ReturnType(ArgType)> aEntry)
 Create a coroutine from a delegate object. More...
 
 ~COROUTINE ()
 
void KiYield ()
 Stop execution of the coroutine and returns control to the caller. More...
 
void KiYield (ReturnType &aRetVal)
 KiYield with a value. More...
 
void RunMainStack (std::function< void()> func)
 Run a functor inside the application main stack context. More...
 
bool Call (ArgType aArg)
 Start execution of a coroutine, passing args as its arguments. More...
 
bool Call (const COROUTINE &aCor, ArgType aArg)
 Start execution of a coroutine, passing args as its arguments. More...
 
bool Resume ()
 Resume execution of a previously yielded coroutine. More...
 
bool Resume (const COROUTINE &aCor)
 Resume execution of a previously yielded coroutine. More...
 
const ReturnType & ReturnValue () const
 Return the yielded value (the argument KiYield() was called with). More...
 
bool Running () const
 

Private Member Functions

INVOCATION_ARGSdoCall (INVOCATION_ARGS *aInvArgs, ArgType aArgs)
 
INVOCATION_ARGSdoResume (INVOCATION_ARGS *args)
 
INVOCATION_ARGSjumpIn (INVOCATION_ARGS *args)
 
void jumpOut ()
 coroutine stack More...
 

Static Private Member Functions

static void callerStub (intptr_t aData)
 

Private Attributes

std::unique_ptr< char[]> m_stack
 
int m_stacksize
 
std::function< ReturnType(ArgType)> m_func
 
bool m_running
 pointer to coroutine entry arguments. More...
 
std::remove_reference< ArgType >::type * m_args
 saved caller context More...
 
CONTEXT_T m_caller
 main stack information More...
 
CALL_CONTEXTm_callContext
 saved coroutine context More...
 
CONTEXT_T m_callee
 
ReturnType m_retVal
 

Detailed Description

template<typename ReturnType, typename ArgType>
class COROUTINE< ReturnType, ArgType >

Implement 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 libcontext 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.

Definition at line 75 of file coroutine.h.

Constructor & Destructor Documentation

◆ COROUTINE() [1/3]

template<typename ReturnType, typename ArgType>
COROUTINE< ReturnType, ArgType >::COROUTINE ( )
inline

Definition at line 172 of file coroutine.h.

172  :
173  COROUTINE( nullptr )
174  {
175  }

◆ COROUTINE() [2/3]

template<typename ReturnType, typename ArgType>
template<class T >
COROUTINE< ReturnType, ArgType >::COROUTINE ( T *  object,
ReturnType(T::*)(ArgType)  ptr 
)
inline

Create a coroutine from a member method of an object.

Definition at line 181 of file coroutine.h.

181  :
182  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
183  {
184  }

◆ COROUTINE() [3/3]

template<typename ReturnType, typename ArgType>
COROUTINE< ReturnType, ArgType >::COROUTINE ( std::function< ReturnType(ArgType)>  aEntry)
inline

Create a coroutine from a delegate object.

Definition at line 189 of file coroutine.h.

189  :
190  m_func( std::move( aEntry ) ),
191  m_running( false ),
192  m_args( nullptr ),
193  m_caller(),
194  m_callContext( nullptr ),
195  m_callee(),
196  m_retVal( 0 )
197 #ifdef KICAD_USE_VALGRIND
198  ,m_valgrind_stack( 0 )
199 #endif
200 #ifdef KICAD_SANITIZE_ADDRESS
201  ,asan_stack( nullptr )
202 #endif
203  {
205  }
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:482
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
CONTEXT_T m_callee
Definition: coroutine.h:497
int m_stacksize
Definition: coroutine.h:480
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:488
ReturnType m_retVal
Definition: coroutine.h:499
int m_CoroutineStackSize
Set the stack size for coroutines.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.

◆ ~COROUTINE()

template<typename ReturnType, typename ArgType>
COROUTINE< ReturnType, ArgType >::~COROUTINE ( )
inline

Definition at line 207 of file coroutine.h.

208  {
209 #ifdef KICAD_USE_VALGRIND
210  VALGRIND_STACK_DEREGISTER( m_valgrind_stack );
211 #endif
212 
213  if( m_caller.ctx )
214  libcontext::release_fcontext( m_caller.ctx );
215 
216  if( m_callee.ctx )
217  libcontext::release_fcontext( m_callee.ctx );
218  }
libcontext::fcontext_t ctx
Definition: coroutine.h:98
CONTEXT_T m_callee
Definition: coroutine.h:497
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491

Member Function Documentation

◆ Call() [1/2]

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::Call ( ArgType  aArg)
inline

Start execution of a coroutine, passing args as its arguments.

Call this method from the application main stack only.

Returns
true if the coroutine has yielded and false if it has finished its execution (returned).

Definition at line 264 of file coroutine.h.

265  {
266  CALL_CONTEXT ctx;
267  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
268 
269 #ifdef KICAD_SANITIZE_THREADS
270  // Get the TSAN fiber for the current stack here
271  m_caller.tsan_fiber = __tsan_get_current_fiber();
272  m_caller.own_tsan_fiber = false;
273 #endif
274 
275  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::Call (from root)" );
276 
277  ctx.Continue( doCall( &args, aArg ) );
278 
279  return Running();
280  }
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:365
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
bool Running() const
Definition: coroutine.h:359

Referenced by TOOL_MANAGER::dispatchInternal().

◆ Call() [2/2]

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::Call ( const COROUTINE< ReturnType, ArgType > &  aCor,
ArgType  aArg 
)
inline

Start execution of a coroutine, passing args as its arguments.

Call this method for a nested coroutine invocation.

Returns
true if the coroutine has yielded and false if it has finished its execution (returned).

Definition at line 290 of file coroutine.h.

291  {
292  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
293 
294  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::Call (from routine)" );
295 
296  doCall( &args, aArg );
297  // we will not be asked to continue
298 
299  return Running();
300  }
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:365
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
bool Running() const
Definition: coroutine.h:359

◆ callerStub()

template<typename ReturnType, typename ArgType>
static void COROUTINE< ReturnType, ArgType >::callerStub ( intptr_t  aData)
inlinestaticprivate

Definition at line 416 of file coroutine.h.

417  {
418  INVOCATION_ARGS& args = *reinterpret_cast<INVOCATION_ARGS*>( aData );
419 
420  // get pointer to self
421  COROUTINE* cor = args.destination;
422  cor->m_callContext = args.context;
423 
424  if( args.type == INVOCATION_ARGS::FROM_ROOT )
425  cor->m_callContext->SetMainStack( &cor->m_caller );
426 
427  // call the coroutine method
428  cor->m_retVal = cor->m_func( *(cor->m_args) );
429  cor->m_running = false;
430 
431  // go back to wherever we came from.
432  cor->jumpOut();
433  }
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:482
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484
Implement a coroutine.
Definition: coroutine.h:75
void jumpOut()
coroutine stack
Definition: coroutine.h:452
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:488
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:137
ReturnType m_retVal
Definition: coroutine.h:499

Referenced by COROUTINE< int, const TOOL_EVENT & >::doCall().

◆ doCall()

template<typename ReturnType, typename ArgType>
INVOCATION_ARGS* COROUTINE< ReturnType, ArgType >::doCall ( INVOCATION_ARGS aInvArgs,
ArgType  aArgs 
)
inlineprivate

Definition at line 365 of file coroutine.h.

366  {
367  assert( m_func );
368  assert( !( m_callee.ctx ) );
369 
370  m_args = &aArgs;
371 
372  assert( m_stack == nullptr );
373 
374  size_t stackSize = m_stacksize;
375  void* sp = nullptr;
376 
377 #ifndef LIBCONTEXT_HAS_OWN_STACK
378 
379  // fixme: Clean up stack stuff. Add a guard
380  m_stack.reset( new char[stackSize] );
381 
382  // align to 16 bytes
383  sp = (void*)((((ptrdiff_t) m_stack.get()) + stackSize - 0xf) & (~0x0f));
384 
385  // correct the stack size
386  stackSize -= size_t( ( (ptrdiff_t) m_stack.get() + stackSize ) - (ptrdiff_t) sp );
387 
388 #ifdef KICAD_USE_VALGRIND
389  m_valgrind_stack = VALGRIND_STACK_REGISTER( sp, m_stack.get() );
390 #endif
391 #endif
392 
393 #ifdef KICAD_SANITIZE_THREADS
394  // Create a new fiber to go with the new context
395  m_callee.tsan_fiber = __tsan_create_fiber( 0 );
396  m_callee.own_tsan_fiber = true;
397 
398  __tsan_set_fiber_name( m_callee.tsan_fiber, "Coroutine fiber" );
399 #endif
400 
401  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::doCall" );
402 
403  m_callee.ctx = libcontext::make_fcontext( sp, stackSize, callerStub );
404  m_running = true;
405 
406  // off we go!
407  return jumpIn( aInvArgs );
408  }
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:482
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484
std::unique_ptr< char[]> m_stack
Definition: coroutine.h:478
libcontext::fcontext_t ctx
Definition: coroutine.h:98
CONTEXT_T m_callee
Definition: coroutine.h:497
int m_stacksize
Definition: coroutine.h:480
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:488
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
static void callerStub(intptr_t aData)
Definition: coroutine.h:416
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:435

Referenced by COROUTINE< int, const TOOL_EVENT & >::Call().

◆ doResume()

template<typename ReturnType, typename ArgType>
INVOCATION_ARGS* COROUTINE< ReturnType, ArgType >::doResume ( INVOCATION_ARGS args)
inlineprivate

Definition at line 410 of file coroutine.h.

411  {
412  return jumpIn( args );
413  }
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:435

Referenced by COROUTINE< ReturnType, ArgType >::CALL_CONTEXT::Continue(), and COROUTINE< int, const TOOL_EVENT & >::Resume().

◆ jumpIn()

template<typename ReturnType, typename ArgType>
INVOCATION_ARGS* COROUTINE< ReturnType, ArgType >::jumpIn ( INVOCATION_ARGS args)
inlineprivate

Definition at line 435 of file coroutine.h.

436  {
437 #ifdef KICAD_SANITIZE_THREADS
438  // Tell TSAN we are changing fibers to the callee
439  __tsan_switch_to_fiber( m_callee.tsan_fiber, 0 );
440 #endif
441 
442  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::jumpIn" );
443 
444  args = reinterpret_cast<INVOCATION_ARGS*>(
445  libcontext::jump_fcontext( &( m_caller.ctx ), m_callee.ctx,
446  reinterpret_cast<intptr_t>( args ) )
447  );
448 
449  return args;
450  }
libcontext::fcontext_t ctx
Definition: coroutine.h:98
CONTEXT_T m_callee
Definition: coroutine.h:497
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.

Referenced by COROUTINE< int, const TOOL_EVENT & >::doCall(), and COROUTINE< int, const TOOL_EVENT & >::doResume().

◆ jumpOut()

template<typename ReturnType, typename ArgType>
void COROUTINE< ReturnType, ArgType >::jumpOut ( )
inlineprivate

◆ KiYield() [1/2]

template<typename ReturnType, typename ArgType>
void COROUTINE< ReturnType, ArgType >::KiYield ( )
inline

Stop 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.

Definition at line 227 of file coroutine.h.

228  {
229  jumpOut();
230  }
void jumpOut()
coroutine stack
Definition: coroutine.h:452

Referenced by TOOL_MANAGER::ScheduleWait().

◆ KiYield() [2/2]

template<typename ReturnType, typename ArgType>
void COROUTINE< ReturnType, ArgType >::KiYield ( ReturnType &  aRetVal)
inline

KiYield with a value.

Passe a value of given type to the caller. Useful for implementing generator objects.

Definition at line 237 of file coroutine.h.

238  {
239  m_retVal = aRetVal;
240  jumpOut();
241  }
void jumpOut()
coroutine stack
Definition: coroutine.h:452
ReturnType m_retVal
Definition: coroutine.h:499

◆ Resume() [1/2]

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::Resume ( )
inline

Resume execution of a previously yielded coroutine.

Call this method only from the main application stack.

Returns
true if the coroutine has yielded again and false if it has finished its execution (returned).

Definition at line 310 of file coroutine.h.

311  {
312  CALL_CONTEXT ctx;
313  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
314 
315 #ifdef KICAD_SANITIZE_THREADS
316  // Get the TSAN fiber for the current stack here
317  m_caller.tsan_fiber = __tsan_get_current_fiber();
318  m_caller.own_tsan_fiber = false;
319 #endif
320 
321  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::Resume (from root)" );
322 
323  ctx.Continue( doResume( &args ) );
324 
325  return Running();
326  }
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:410
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
bool Running() const
Definition: coroutine.h:359

Referenced by TOOL_MANAGER::dispatchInternal().

◆ Resume() [2/2]

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::Resume ( const COROUTINE< ReturnType, ArgType > &  aCor)
inline

Resume execution of a previously yielded coroutine.

Call this method for a nested coroutine invocation.

Returns
true if the coroutine has yielded again and false if it has finished its execution (returned).

Definition at line 336 of file coroutine.h.

337  {
338  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
339 
340  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::Resume (from routine)" );
341 
342  doResume( &args );
343  // we will not be asked to continue
344 
345  return Running();
346  }
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:410
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
bool Running() const
Definition: coroutine.h:359

◆ ReturnValue()

template<typename ReturnType, typename ArgType>
const ReturnType& COROUTINE< ReturnType, ArgType >::ReturnValue ( ) const
inline

Return the yielded value (the argument KiYield() was called with).

Definition at line 351 of file coroutine.h.

352  {
353  return m_retVal;
354  }
ReturnType m_retVal
Definition: coroutine.h:499

◆ RunMainStack()

template<typename ReturnType, typename ArgType>
void COROUTINE< ReturnType, ArgType >::RunMainStack ( std::function< void()>  func)
inline

Run a functor inside the application main stack context.

Call this function for example if the operation will spawn a webkit browser instance which will walk the stack to the upper border of the address space on mac osx systems because its javascript needs garbage collection (for example if you paste text into an edit box).

Definition at line 250 of file coroutine.h.

251  {
252  assert( m_callContext );
253  m_callContext->RunMainStack( this, std::move( func ) );
254  }
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:142
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494

Referenced by TOOL_MANAGER::RunMainStack().

◆ Running()

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::Running ( ) const
inline
Returns
true if the coroutine is active.

Definition at line 359 of file coroutine.h.

360  {
361  return m_running;
362  }
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484

Referenced by COROUTINE< int, const TOOL_EVENT & >::Call(), TOOL_MANAGER::dispatchInternal(), and COROUTINE< int, const TOOL_EVENT & >::Resume().

Member Data Documentation

◆ m_args

template<typename ReturnType, typename ArgType>
std::remove_reference<ArgType>::type* COROUTINE< ReturnType, ArgType >::m_args
private

saved caller context

Definition at line 488 of file coroutine.h.

Referenced by COROUTINE< int, const TOOL_EVENT & >::callerStub(), and COROUTINE< int, const TOOL_EVENT & >::doCall().

◆ m_callContext

◆ m_callee

◆ m_caller

◆ m_func

template<typename ReturnType, typename ArgType>
std::function<ReturnType( ArgType )> COROUTINE< ReturnType, ArgType >::m_func
private

◆ m_retVal

template<typename ReturnType, typename ArgType>
ReturnType COROUTINE< ReturnType, ArgType >::m_retVal
private

◆ m_running

template<typename ReturnType, typename ArgType>
bool COROUTINE< ReturnType, ArgType >::m_running
private

pointer to coroutine entry arguments.

Stripped of references to avoid compiler errors.

Definition at line 484 of file coroutine.h.

Referenced by COROUTINE< int, const TOOL_EVENT & >::callerStub(), COROUTINE< int, const TOOL_EVENT & >::doCall(), and COROUTINE< int, const TOOL_EVENT & >::Running().

◆ m_stack

template<typename ReturnType, typename ArgType>
std::unique_ptr<char[]> COROUTINE< ReturnType, ArgType >::m_stack
private

Definition at line 478 of file coroutine.h.

Referenced by COROUTINE< int, const TOOL_EVENT & >::doCall().

◆ m_stacksize

template<typename ReturnType, typename ArgType>
int COROUTINE< ReturnType, ArgType >::m_stacksize
private

The documentation for this class was generated from the following file: