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 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 <cstdlib>
30 
31 #include <type_traits>
32 
33 #include <system/libcontext.h>
34 #include <memory>
35 
57 template <typename ReturnType, typename ArgType>
58 class COROUTINE
59 {
60 private:
61  class CALL_CONTEXT;
62 
64  {
65  enum
66  {
67  FROM_ROOT, // a stub was called/a corutine was resumed from the main-stack context
68  FROM_ROUTINE, // a stub was called/a coroutine was resumed fron a coroutine context
69  CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
70  // stack context
71  } type; // invocation type
72  COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
73  // ptr for the coroutine to be resumed if a
74  // root(main-stack)-call-was initiated.
75  CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
76  // call context holds a reference to the main stack context
77  };
78 
81 
83  {
84  public:
85  void SetMainStack( CONTEXT_T* aStack )
86  {
87  m_mainStackContext = aStack;
88  }
89 
90  void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
91  {
92  m_mainStackFunction = std::move( aFunc );
94 
96  reinterpret_cast<intptr_t>( &args ) );
97  }
98 
99  void Continue( INVOCATION_ARGS* args )
100  {
102  {
105  args = args->destination->doResume( args );
106  }
107  }
108 
109  private:
111  std::function<void()> m_mainStackFunction;
112  };
113 
114 public:
116  COROUTINE( nullptr )
117  {
118  }
119 
124  template <class T>
125  COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
126  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
127  {
128  }
129 
134  COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
135  m_func( std::move( aEntry ) ),
136  m_running( false ),
137  m_args( 0 ),
138  m_callee( nullptr ),
139  m_retVal( 0 )
140  {
141  }
142 
144  {
145  }
146 
147 public:
155  void KiYield()
156  {
157  jumpOut();
158  }
159 
166  void KiYield( ReturnType& aRetVal )
167  {
168  m_retVal = aRetVal;
169  jumpOut();
170  }
171 
177  void SetEntry( std::function<ReturnType(ArgType)> aEntry )
178  {
179  m_func = std::move( aEntry );
180  }
181 
190  void RunMainStack( std::function<void()> func )
191  {
192  assert( m_callContext );
193  m_callContext->RunMainStack( this, std::move( func ) );
194  }
195 
204  bool Call( ArgType aArg )
205  {
206  CALL_CONTEXT ctx;
207  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
208  ctx.Continue( doCall( &args, aArg ) );
209 
210  return Running();
211  }
212 
221  bool Call( const COROUTINE& aCor, ArgType aArg )
222  {
223  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
224  doCall( &args, aArg );
225  // we will not be asked to continue
226 
227  return Running();
228  }
229 
238  bool Resume()
239  {
240  CALL_CONTEXT ctx;
241  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
242  ctx.Continue( doResume( &args ) );
243 
244  return Running();
245  }
246 
255  bool Resume( const COROUTINE& aCor )
256  {
257  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
258  doResume( &args );
259  // we will not be asked to continue
260 
261  return Running();
262  }
263 
269  const ReturnType& ReturnValue() const
270  {
271  return m_retVal;
272  }
273 
279  bool Running() const
280  {
281  return m_running;
282  }
283 
284 private:
285  INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
286  {
287  assert( m_func );
288  assert( !m_callee );
289 
290  m_args = &aArgs;
291 
292  assert( m_stack == nullptr );
293 
294  // fixme: Clean up stack stuff. Add a guard
295  size_t stackSize = c_defaultStackSize;
296  m_stack.reset( new char[stackSize] );
297 
298  // align to 16 bytes
299  void* sp = (void*)((((ptrdiff_t) m_stack.get()) + stackSize - 0xf) & (~0x0f));
300 
301  // correct the stack size
302  stackSize -= size_t( ( (ptrdiff_t) m_stack.get() + stackSize ) - (ptrdiff_t) sp );
303 
304  m_callee = libcontext::make_fcontext( sp, stackSize, callerStub );
305  m_running = true;
306 
307  // off we go!
308  return jumpIn( aInvArgs );
309  }
310 
311  INVOCATION_ARGS* doResume( INVOCATION_ARGS* args )
312  {
313  return jumpIn( args );
314  }
315 
316  /* real entry point of the coroutine */
317  static void callerStub( intptr_t aData )
318  {
319  INVOCATION_ARGS& args = *reinterpret_cast<INVOCATION_ARGS*>( aData );
320  // get pointer to self
321  COROUTINE* cor = args.destination;
322  cor->m_callContext = args.context;
323 
324  if( args.type == INVOCATION_ARGS::FROM_ROOT )
325  cor->m_callContext->SetMainStack( &cor->m_caller );
326 
327  // call the coroutine method
328  cor->m_retVal = cor->m_func( *(cor->m_args) );
329  cor->m_running = false;
330 
331  // go back to wherever we came from.
332  cor->jumpOut();
333  }
334 
335  INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
336  {
337  args = reinterpret_cast<INVOCATION_ARGS*>(
339  reinterpret_cast<intptr_t>( args ) )
340  );
341 
342  return args;
343  }
344 
345  void jumpOut()
346  {
347  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
348  INVOCATION_ARGS* ret;
349  ret = reinterpret_cast<INVOCATION_ARGS*>(
351  reinterpret_cast<intptr_t>( &args ) )
352  );
353 
354  m_callContext = ret->context;
355 
356  if( ret->type == INVOCATION_ARGS::FROM_ROOT )
357  {
359  }
360  }
361 
362  static constexpr int c_defaultStackSize = 2000000; // fixme: make configurable
363 
365  std::unique_ptr<char[]> m_stack;
366 
367  std::function<ReturnType( ArgType )> m_func;
368 
369  bool m_running;
370 
373  typename std::remove_reference<ArgType>::type* m_args;
374 
377 
379  CALL_CONTEXT* m_callContext;
380 
383 
384  ReturnType m_retVal;
385 };
386 
387 #endif
void RunMainStack(std::function< void()> func)
Function RunMainStack()
Definition: coroutine.h:190
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:367
void KiYield(ReturnType &aRetVal)
Function KiYield()
Definition: coroutine.h:166
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:369
Class COROUNTINE.
Definition: coroutine.h:58
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:90
void jumpOut()
Definition: coroutine.h:345
T
enum T contains all this lexer's tokens.
bool Call(ArgType aArg)
Function Call()
Definition: coroutine.h:204
void * fcontext_t
Definition: libcontext.h:76
bool Resume(const COROUTINE &aCor)
Function Resume()
Definition: coroutine.h:255
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:379
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:110
std::unique_ptr< char[]> m_stack
< coroutine stack
Definition: coroutine.h:365
bool Call(const COROUTINE &aCor, ArgType aArg)
Function Call()
Definition: coroutine.h:221
intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext(fcontext_t *ofc, fcontext_t nfc, intptr_t vp, bool preserve_fpu=false)
std::function< void()> m_mainStackFunction
Definition: coroutine.h:111
static constexpr int c_defaultStackSize
Definition: coroutine.h:362
const ReturnType & ReturnValue() const
Function ReturnValue()
Definition: coroutine.h:269
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:376
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:285
bool Resume()
Function Resume()
Definition: coroutine.h:238
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:373
CALLEE_STORAGE m_callee
Definition: coroutine.h:382
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:85
ReturnType m_retVal
Definition: coroutine.h:384
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:311
CONTEXT_T CALLEE_STORAGE
Definition: coroutine.h:80
~COROUTINE()
Definition: coroutine.h:143
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:99
static void callerStub(intptr_t aData)
Definition: coroutine.h:317
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:335
void SetEntry(std::function< ReturnType(ArgType)> aEntry)
Function SetEntry()
Definition: coroutine.h:177
CALL_CONTEXT * context
Definition: coroutine.h:75
libcontext::fcontext_t CONTEXT_T
Definition: coroutine.h:79
bool Running() const
Function Running()
Definition: coroutine.h:279
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Constructor Creates a coroutine from a delegate object.
Definition: coroutine.h:134
void KiYield()
Function KiYield()
Definition: coroutine.h:155
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Constructor Creates a coroutine from a member method of an object.
Definition: coroutine.h:125
enum COROUTINE::INVOCATION_ARGS::@53 type