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 
32 #include <type_traits>
33 
34 #include <system/libcontext.h>
35 #include <memory>
36 
58 template <typename ReturnType, typename ArgType>
59 class COROUTINE
60 {
61 private:
62  class CALL_CONTEXT;
63 
65  {
66  enum
67  {
68  FROM_ROOT, // a stub was called/a corutine was resumed from the main-stack context
69  FROM_ROUTINE, // a stub was called/a coroutine was resumed fron a coroutine context
70  CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
71  // stack context
72  } type; // invocation type
73  COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
74  // ptr for the coroutine to be resumed if a
75  // root(main-stack)-call-was initiated.
76  CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
77  // call context holds a reference to the main stack context
78  };
79 
82 
84  {
85  public:
86  void SetMainStack( CONTEXT_T* aStack )
87  {
88  m_mainStackContext = aStack;
89  }
90 
91  void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
92  {
93  m_mainStackFunction = std::move( aFunc );
95 
97  reinterpret_cast<intptr_t>( &args ) );
98  }
99 
100  void Continue( INVOCATION_ARGS* args )
101  {
103  {
106  args = args->destination->doResume( args );
107  }
108  }
109 
110  private:
112  std::function<void()> m_mainStackFunction;
113  };
114 
115 public:
117  COROUTINE( nullptr )
118  {
119  }
120 
125  template <class T>
126  COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
127  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
128  {
129  }
130 
135  COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
136  m_func( std::move( aEntry ) ),
137  m_running( false ),
138  m_args( 0 ),
139  m_caller( nullptr ),
140  m_callContext( nullptr ),
141  m_callee( nullptr ),
142  m_retVal( 0 )
143  {
144  }
145 
147  {
148  }
149 
150 public:
158  void KiYield()
159  {
160  jumpOut();
161  }
162 
169  void KiYield( ReturnType& aRetVal )
170  {
171  m_retVal = aRetVal;
172  jumpOut();
173  }
174 
180  void SetEntry( std::function<ReturnType(ArgType)> aEntry )
181  {
182  m_func = std::move( aEntry );
183  }
184 
193  void RunMainStack( std::function<void()> func )
194  {
195  assert( m_callContext );
196  m_callContext->RunMainStack( this, std::move( func ) );
197  }
198 
207  bool Call( ArgType aArg )
208  {
209  CALL_CONTEXT ctx;
210  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
211  ctx.Continue( doCall( &args, aArg ) );
212 
213  return Running();
214  }
215 
224  bool Call( const COROUTINE& aCor, ArgType aArg )
225  {
226  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
227  doCall( &args, aArg );
228  // we will not be asked to continue
229 
230  return Running();
231  }
232 
241  bool Resume()
242  {
243  CALL_CONTEXT ctx;
244  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
245  ctx.Continue( doResume( &args ) );
246 
247  return Running();
248  }
249 
258  bool Resume( const COROUTINE& aCor )
259  {
260  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
261  doResume( &args );
262  // we will not be asked to continue
263 
264  return Running();
265  }
266 
272  const ReturnType& ReturnValue() const
273  {
274  return m_retVal;
275  }
276 
282  bool Running() const
283  {
284  return m_running;
285  }
286 
287 private:
288  INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
289  {
290  assert( m_func );
291  assert( !m_callee );
292 
293  m_args = &aArgs;
294 
295  assert( m_stack == nullptr );
296 
297  // fixme: Clean up stack stuff. Add a guard
298  size_t stackSize = c_defaultStackSize;
299  m_stack.reset( new char[stackSize] );
300 
301  // align to 16 bytes
302  void* sp = (void*)((((ptrdiff_t) m_stack.get()) + stackSize - 0xf) & (~0x0f));
303 
304  // correct the stack size
305  stackSize -= size_t( ( (ptrdiff_t) m_stack.get() + stackSize ) - (ptrdiff_t) sp );
306 
307  m_callee = libcontext::make_fcontext( sp, stackSize, callerStub );
308  m_running = true;
309 
310  // off we go!
311  return jumpIn( aInvArgs );
312  }
313 
314  INVOCATION_ARGS* doResume( INVOCATION_ARGS* args )
315  {
316  return jumpIn( args );
317  }
318 
319  /* real entry point of the coroutine */
320  static void callerStub( intptr_t aData )
321  {
322  INVOCATION_ARGS& args = *reinterpret_cast<INVOCATION_ARGS*>( aData );
323  // get pointer to self
324  COROUTINE* cor = args.destination;
325  cor->m_callContext = args.context;
326 
327  if( args.type == INVOCATION_ARGS::FROM_ROOT )
328  cor->m_callContext->SetMainStack( &cor->m_caller );
329 
330  // call the coroutine method
331  cor->m_retVal = cor->m_func( *(cor->m_args) );
332  cor->m_running = false;
333 
334  // go back to wherever we came from.
335  cor->jumpOut();
336  }
337 
338  INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
339  {
340  args = reinterpret_cast<INVOCATION_ARGS*>(
342  reinterpret_cast<intptr_t>( args ) )
343  );
344 
345  return args;
346  }
347 
348  void jumpOut()
349  {
350  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
351  INVOCATION_ARGS* ret;
352  ret = reinterpret_cast<INVOCATION_ARGS*>(
354  reinterpret_cast<intptr_t>( &args ) )
355  );
356 
357  m_callContext = ret->context;
358 
359  if( ret->type == INVOCATION_ARGS::FROM_ROOT )
360  {
362  }
363  }
364 
365  static constexpr int c_defaultStackSize = 2000000; // fixme: make configurable
366 
368  std::unique_ptr<char[]> m_stack;
369 
370  std::function<ReturnType( ArgType )> m_func;
371 
372  bool m_running;
373 
376  typename std::remove_reference<ArgType>::type* m_args;
377 
380 
382  CALL_CONTEXT* m_callContext;
383 
386 
387  ReturnType m_retVal;
388 };
389 
390 #endif
void RunMainStack(std::function< void()> func)
Function RunMainStack()
Definition: coroutine.h:193
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:370
void KiYield(ReturnType &aRetVal)
Function KiYield()
Definition: coroutine.h:169
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:372
Class COROUNTINE.
Definition: coroutine.h:59
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:91
void jumpOut()
Definition: coroutine.h:348
bool Call(ArgType aArg)
Function Call()
Definition: coroutine.h:207
void * fcontext_t
Definition: libcontext.h:86
bool Resume(const COROUTINE &aCor)
Function Resume()
Definition: coroutine.h:258
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:382
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:111
std::unique_ptr< char[]> m_stack
< coroutine stack
Definition: coroutine.h:368
bool Call(const COROUTINE &aCor, ArgType aArg)
Function Call()
Definition: coroutine.h:224
Template specialization to enable wxStrings for certain containers (e.g. unordered_map)
std::function< void()> m_mainStackFunction
Definition: coroutine.h:112
static constexpr int c_defaultStackSize
Definition: coroutine.h:365
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:379
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:288
bool Resume()
Function Resume()
Definition: coroutine.h:241
enum COROUTINE::INVOCATION_ARGS::@67 type
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:376
CALLEE_STORAGE m_callee
Definition: coroutine.h:385
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:86
const ReturnType & ReturnValue() const
Function ReturnValue()
Definition: coroutine.h:272
ReturnType m_retVal
Definition: coroutine.h:387
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:314
~COROUTINE()
Definition: coroutine.h:146
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:100
static void callerStub(intptr_t aData)
Definition: coroutine.h:320
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:338
void SetEntry(std::function< ReturnType(ArgType)> aEntry)
Function SetEntry()
Definition: coroutine.h:180
bool Running() const
Function Running()
Definition: coroutine.h:282
CALL_CONTEXT * context
Definition: coroutine.h:76
libcontext::fcontext_t CONTEXT_T
Definition: coroutine.h:80
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Constructor Creates a coroutine from a delegate object.
Definition: coroutine.h:135
void KiYield()
Function KiYield()
Definition: coroutine.h:158
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Constructor Creates a coroutine from a member method of an object.
Definition: coroutine.h:126