KiCad PCB EDA Suite
coroutine.h
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2013 CERN
5  * Copyright (C) 2016-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
27 #ifndef __COROUTINE_H
28 #define __COROUTINE_H
29 
30 #include <cassert>
31 #include <cstdlib>
32 #include <type_traits>
33 
34 #ifdef KICAD_USE_VALGRIND
35 #include <valgrind/valgrind.h>
36 #endif
37 
38 #include <libcontext.h>
39 #include <memory>
40 #include <advanced_config.h>
41 
64 template <typename ReturnType, typename ArgType>
65 class COROUTINE
66 {
67 private:
68  class CALL_CONTEXT;
69 
71  {
72  enum
73  {
74  FROM_ROOT, // a stub was called/a coroutine was resumed from the main-stack context
75  FROM_ROUTINE, // a stub was called/a coroutine was resumed from a coroutine context
76  CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
77  // stack context
78  } type; // invocation type
79  COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
80  // ptr for the coroutine to be resumed if a
81  // root(main-stack)-call-was initiated.
82  CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
83  // call context holds a reference to the main stack context
84  };
85 
86  using CONTEXT_T = libcontext::fcontext_t;
88 
90  {
91  public:
93  m_mainStackContext( nullptr )
94  {
95  }
96 
98  {
99  if ( m_mainStackContext )
100  libcontext::release_fcontext( *m_mainStackContext );
101  }
102 
103 
104  void SetMainStack( CONTEXT_T* aStack )
105  {
106  m_mainStackContext = aStack;
107  }
108 
109  void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
110  {
111  m_mainStackFunction = std::move( aFunc );
113 
114  libcontext::jump_fcontext( &aCor->m_callee, *m_mainStackContext,
115  reinterpret_cast<intptr_t>( &args ) );
116  }
117 
118  void Continue( INVOCATION_ARGS* args )
119  {
121  {
124  args = args->destination->doResume( args );
125  }
126  }
127 
128  private:
130  std::function<void()> m_mainStackFunction;
131  };
132 
133 public:
135  COROUTINE( nullptr )
136  {
137  }
138 
142  template <class T>
143  COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
144  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
145  {
146  }
147 
151  COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
152  m_func( std::move( aEntry ) ),
153  m_running( false ),
154  m_args( 0 ),
155  m_caller( nullptr ),
156  m_callContext( nullptr ),
157  m_callee( nullptr ),
158  m_retVal( 0 )
159 #ifdef KICAD_USE_VALGRIND
160  ,valgrind_stack( 0 )
161 #endif
162  {
164  }
165 
167  {
168 #ifdef KICAD_USE_VALGRIND
169  VALGRIND_STACK_DEREGISTER( valgrind_stack );
170 #endif
171  if(m_caller)
172  libcontext::release_fcontext( m_caller );
173  if(m_callee)
174  libcontext::release_fcontext( m_callee );
175  }
176 
177 public:
184  void KiYield()
185  {
186  jumpOut();
187  }
188 
194  void KiYield( ReturnType& aRetVal )
195  {
196  m_retVal = aRetVal;
197  jumpOut();
198  }
199 
207  void RunMainStack( std::function<void()> func )
208  {
209  assert( m_callContext );
210  m_callContext->RunMainStack( this, std::move( func ) );
211  }
212 
221  bool Call( ArgType aArg )
222  {
223  CALL_CONTEXT ctx;
224  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
225  ctx.Continue( doCall( &args, aArg ) );
226 
227  return Running();
228  }
229 
238  bool Call( const COROUTINE& aCor, ArgType aArg )
239  {
240  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
241  doCall( &args, aArg );
242  // we will not be asked to continue
243 
244  return Running();
245  }
246 
255  bool Resume()
256  {
257  CALL_CONTEXT ctx;
258  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
259  ctx.Continue( doResume( &args ) );
260 
261  return Running();
262  }
263 
272  bool Resume( const COROUTINE& aCor )
273  {
274  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
275  doResume( &args );
276  // we will not be asked to continue
277 
278  return Running();
279  }
280 
284  const ReturnType& ReturnValue() const
285  {
286  return m_retVal;
287  }
288 
292  bool Running() const
293  {
294  return m_running;
295  }
296 
297 private:
298  INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
299  {
300  assert( m_func );
301  assert( !m_callee );
302 
303  m_args = &aArgs;
304 
305  assert( m_stack == nullptr );
306 
307  size_t stackSize = m_stacksize;
308  void* sp = nullptr;
309 
310 #ifndef LIBCONTEXT_HAS_OWN_STACK
311 
312  // fixme: Clean up stack stuff. Add a guard
313  m_stack.reset( new char[stackSize] );
314 
315  // align to 16 bytes
316  sp = (void*)((((ptrdiff_t) m_stack.get()) + stackSize - 0xf) & (~0x0f));
317 
318  // correct the stack size
319  stackSize -= size_t( ( (ptrdiff_t) m_stack.get() + stackSize ) - (ptrdiff_t) sp );
320 
321 #ifdef KICAD_USE_VALGRIND
322  valgrind_stack = VALGRIND_STACK_REGISTER( sp, m_stack.get() );
323 #endif
324 #endif
325 
326  m_callee = libcontext::make_fcontext( sp, stackSize, callerStub );
327  m_running = true;
328 
329  // off we go!
330  return jumpIn( aInvArgs );
331  }
332 
333  INVOCATION_ARGS* doResume( INVOCATION_ARGS* args )
334  {
335  return jumpIn( args );
336  }
337 
338  /* real entry point of the coroutine */
339  static void callerStub( intptr_t aData )
340  {
341  INVOCATION_ARGS& args = *reinterpret_cast<INVOCATION_ARGS*>( aData );
342 
343  // get pointer to self
344  COROUTINE* cor = args.destination;
345  cor->m_callContext = args.context;
346 
347  if( args.type == INVOCATION_ARGS::FROM_ROOT )
348  cor->m_callContext->SetMainStack( &cor->m_caller );
349 
350  // call the coroutine method
351  cor->m_retVal = cor->m_func( *(cor->m_args) );
352  cor->m_running = false;
353 
354  // go back to wherever we came from.
355  cor->jumpOut();
356  }
357 
358  INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
359  {
360  args = reinterpret_cast<INVOCATION_ARGS*>(
361  libcontext::jump_fcontext( &m_caller, m_callee,
362  reinterpret_cast<intptr_t>( args ) )
363  );
364 
365  return args;
366  }
367 
368  void jumpOut()
369  {
370  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
371  INVOCATION_ARGS* ret;
372 
373  ret = reinterpret_cast<INVOCATION_ARGS*>(
374  libcontext::jump_fcontext( &m_callee, m_caller,
375  reinterpret_cast<intptr_t>( &args ) )
376  );
377 
378  m_callContext = ret->context;
379 
380  if( ret->type == INVOCATION_ARGS::FROM_ROOT )
381  {
383  }
384  }
385 
387  std::unique_ptr<char[]> m_stack;
388 
390 
391  std::function<ReturnType( ArgType )> m_func;
392 
393  bool m_running;
394 
397  typename std::remove_reference<ArgType>::type* m_args;
398 
401 
403  CALL_CONTEXT* m_callContext;
404 
407 
408  ReturnType m_retVal;
409 
410 #ifdef KICAD_USE_VALGRIND
411  uint32_t valgrind_stack;
412 #endif
413 };
414 
415 #endif
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
Definition: coroutine.h:207
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:391
void KiYield(ReturnType &aRetVal)
KiYield with a value.
Definition: coroutine.h:194
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:393
Implement a coroutine.
Definition: coroutine.h:65
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:109
void jumpOut()
coroutine stack
Definition: coroutine.h:368
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:221
bool Resume(const COROUTINE &aCor)
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:272
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:403
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:129
std::unique_ptr< char[]> m_stack
Definition: coroutine.h:387
bool Call(const COROUTINE &aCor, ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:238
Definition: bitmap.cpp:58
std::function< void()> m_mainStackFunction
Definition: coroutine.h:130
int m_stacksize
Definition: coroutine.h:389
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:400
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:298
bool Resume()
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:255
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:397
CALLEE_STORAGE m_callee
Definition: coroutine.h:406
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:104
const ReturnType & ReturnValue() const
Return the yielded value (the argument KiYield() was called with).
Definition: coroutine.h:284
ReturnType m_retVal
Definition: coroutine.h:408
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:333
int m_CoroutineStackSize
Set the stack size for coroutines.
~COROUTINE()
Definition: coroutine.h:166
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:118
static void callerStub(intptr_t aData)
Definition: coroutine.h:339
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:358
bool Running() const
Definition: coroutine.h:292
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CALL_CONTEXT * context
Definition: coroutine.h:82
libcontext::fcontext_t CONTEXT_T
Definition: coroutine.h:86
enum COROUTINE::INVOCATION_ARGS::@32 type
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Create a coroutine from a delegate object.
Definition: coroutine.h:151
void KiYield()
Stop execution of the coroutine and returns control to the caller.
Definition: coroutine.h:184
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Create a coroutine from a member method of an object.
Definition: coroutine.h:143