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  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
6  * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #ifndef __COROUTINE_H
27 #define __COROUTINE_H
28 
29 #include <cassert>
30 #include <cstdlib>
31 #include <type_traits>
32 
33 #ifdef KICAD_USE_VALGRIND
34 #include <valgrind/valgrind.h>
35 #endif
36 
37 #include <advanced_config.h>
38 #include <system/libcontext.h>
39 #include <memory>
40 
62 template <typename ReturnType, typename ArgType>
63 class COROUTINE
64 {
65 private:
66  class CALL_CONTEXT;
67 
69  {
70  enum
71  {
72  FROM_ROOT, // a stub was called/a coroutine was resumed from the main-stack context
73  FROM_ROUTINE, // a stub was called/a coroutine was resumed from a coroutine context
74  CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
75  // stack context
76  } type; // invocation type
77  COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
78  // ptr for the coroutine to be resumed if a
79  // root(main-stack)-call-was initiated.
80  CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
81  // call context holds a reference to the main stack context
82  };
83 
86 
88  {
89  public:
90  void SetMainStack( CONTEXT_T* aStack )
91  {
92  m_mainStackContext = aStack;
93  }
94 
95  void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
96  {
97  m_mainStackFunction = std::move( aFunc );
99 
101  reinterpret_cast<intptr_t>( &args ) );
102  }
103 
104  void Continue( INVOCATION_ARGS* args )
105  {
107  {
110  args = args->destination->doResume( args );
111  }
112  }
113 
114  private:
116  std::function<void()> m_mainStackFunction;
117  };
118 
119 public:
121  COROUTINE( nullptr )
122  {
123  }
124 
129  template <class T>
130  COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
131  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
132  {
133  }
134 
139  COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
140  m_func( std::move( aEntry ) ),
141  m_running( false ),
142  m_args( 0 ),
143  m_caller( nullptr ),
144  m_callContext( nullptr ),
145  m_callee( nullptr ),
146  m_retVal( 0 )
147 #ifdef KICAD_USE_VALGRIND
148  ,valgrind_stack( 0 )
149 #endif
150  {
152  }
153 
155  {
156 #ifdef KICAD_USE_VALGRIND
157  VALGRIND_STACK_DEREGISTER( valgrind_stack );
158 #endif
159  }
160 
161 public:
169  void KiYield()
170  {
171  jumpOut();
172  }
173 
180  void KiYield( ReturnType& aRetVal )
181  {
182  m_retVal = aRetVal;
183  jumpOut();
184  }
185 
191  void SetEntry( std::function<ReturnType(ArgType)> aEntry )
192  {
193  m_func = std::move( aEntry );
194  }
195 
204  void RunMainStack( std::function<void()> func )
205  {
206  assert( m_callContext );
207  m_callContext->RunMainStack( this, std::move( func ) );
208  }
209 
218  bool Call( ArgType aArg )
219  {
220  CALL_CONTEXT ctx;
221  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
222  ctx.Continue( doCall( &args, aArg ) );
223 
224  return Running();
225  }
226 
235  bool Call( const COROUTINE& aCor, ArgType aArg )
236  {
237  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
238  doCall( &args, aArg );
239  // we will not be asked to continue
240 
241  return Running();
242  }
243 
252  bool Resume()
253  {
254  CALL_CONTEXT ctx;
255  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
256  ctx.Continue( doResume( &args ) );
257 
258  return Running();
259  }
260 
269  bool Resume( const COROUTINE& aCor )
270  {
271  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
272  doResume( &args );
273  // we will not be asked to continue
274 
275  return Running();
276  }
277 
283  const ReturnType& ReturnValue() const
284  {
285  return m_retVal;
286  }
287 
293  bool Running() const
294  {
295  return m_running;
296  }
297 
298 private:
299  INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
300  {
301  assert( m_func );
302  assert( !m_callee );
303 
304  m_args = &aArgs;
305 
306  assert( m_stack == nullptr );
307 
308  size_t stackSize = m_stacksize;
309  void* sp = nullptr;
310 
311  #ifndef LIBCONTEXT_HAS_OWN_STACK
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  // get pointer to self
343  COROUTINE* cor = args.destination;
344  cor->m_callContext = args.context;
345 
346  if( args.type == INVOCATION_ARGS::FROM_ROOT )
347  cor->m_callContext->SetMainStack( &cor->m_caller );
348 
349  // call the coroutine method
350  cor->m_retVal = cor->m_func( *(cor->m_args) );
351  cor->m_running = false;
352 
353  // go back to wherever we came from.
354  cor->jumpOut();
355  }
356 
357  INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
358  {
359  args = reinterpret_cast<INVOCATION_ARGS*>(
361  reinterpret_cast<intptr_t>( args ) )
362  );
363 
364  return args;
365  }
366 
367  void jumpOut()
368  {
369  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
370  INVOCATION_ARGS* ret;
371  ret = reinterpret_cast<INVOCATION_ARGS*>(
373  reinterpret_cast<intptr_t>( &args ) )
374  );
375 
376  m_callContext = ret->context;
377 
378  if( ret->type == INVOCATION_ARGS::FROM_ROOT )
379  {
381  }
382  }
383 
385  std::unique_ptr<char[]> m_stack;
386 
388 
389  std::function<ReturnType( ArgType )> m_func;
390 
391  bool m_running;
392 
395  typename std::remove_reference<ArgType>::type* m_args;
396 
399 
401  CALL_CONTEXT* m_callContext;
402 
405 
406  ReturnType m_retVal;
407 
408 #ifdef KICAD_USE_VALGRIND
409  uint32_t valgrind_stack;
410 #endif
411 };
412 
413 #endif
void RunMainStack(std::function< void()> func)
Function RunMainStack()
Definition: coroutine.h:204
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:389
void KiYield(ReturnType &aRetVal)
Function KiYield()
Definition: coroutine.h:180
intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu=true)
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:391
Class COROUNTINE.
Definition: coroutine.h:63
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:95
void jumpOut()
coroutine stack
Definition: coroutine.h:367
bool Call(ArgType aArg)
Function Call()
Definition: coroutine.h:218
void * fcontext_t
Definition: libcontext.h:99
bool Resume(const COROUTINE &aCor)
Function Resume()
Definition: coroutine.h:269
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:401
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:115
std::unique_ptr< char[]> m_stack
Definition: coroutine.h:385
bool Call(const COROUTINE &aCor, ArgType aArg)
Function Call()
Definition: coroutine.h:235
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
std::function< void()> m_mainStackFunction
Definition: coroutine.h:116
int m_stacksize
Definition: coroutine.h:387
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:398
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:299
bool Resume()
Function Resume()
Definition: coroutine.h:252
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:395
CALLEE_STORAGE m_callee
Definition: coroutine.h:404
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:90
const ReturnType & ReturnValue() const
Function ReturnValue()
Definition: coroutine.h:283
int m_coroutineStackSize
Set the stack size for coroutines.
ReturnType m_retVal
Definition: coroutine.h:406
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:333
~COROUTINE()
Definition: coroutine.h:154
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:104
static void callerStub(intptr_t aData)
Definition: coroutine.h:339
fcontext_t LIBCONTEXT_CALL_CONVENTION make_fcontext(void *sp, size_t size, void(*fn)(intptr_t))
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:357
void SetEntry(std::function< ReturnType(ArgType)> aEntry)
Function SetEntry()
Definition: coroutine.h:191
enum COROUTINE::INVOCATION_ARGS::@66 type
bool Running() const
Function Running()
Definition: coroutine.h:293
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
CALL_CONTEXT * context
Definition: coroutine.h:80
libcontext::fcontext_t CONTEXT_T
Definition: coroutine.h:84
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Constructor Creates a coroutine from a delegate object.
Definition: coroutine.h:139
void KiYield()
Function KiYield()
Definition: coroutine.h:169
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Constructor Creates a coroutine from a member method of an object.
Definition: coroutine.h:130