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 #ifdef KICAD_SANITIZE_THREADS
38 #include <sanitizer/tsan_interface.h>
39 #endif
40 #ifdef KICAD_SANITIZE_ADDRESS
41 #include <sanitizer/asan_interface.h>
42 #endif
43 
44 #include <libcontext.h>
45 #include <functional>
46 #include <memory>
47 #include <advanced_config.h>
48 
49 #include <trace_helpers.h>
50 #include <wx/log.h>
51 
74 template <typename ReturnType, typename ArgType>
75 class COROUTINE
76 {
77 private:
78  class CALL_CONTEXT;
79 
81  {
82  enum
83  {
84  FROM_ROOT, // a stub was called/a coroutine was resumed from the main-stack context
85  FROM_ROUTINE, // a stub was called/a coroutine was resumed from a coroutine context
86  CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
87  // stack context
88  } type; // invocation type
89  COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
90  // ptr for the coroutine to be resumed if a
91  // root(main-stack)-call-was initiated.
92  CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
93  // call context holds a reference to the main stack context
94  };
95 
96  struct CONTEXT_T
97  {
98  libcontext::fcontext_t ctx; // The context itself
99 #ifdef KICAD_SANITIZE_THREADS
100  void* tsan_fiber; // The TSAN fiber for this context
101  bool own_tsan_fiber; // Do we own this TSAN fiber? (we only delete fibers we own)
102 #endif
103 
105  ctx( nullptr )
106 #ifdef KICAD_SANITIZE_THREADS
107  ,tsan_fiber( nullptr )
108  ,own_tsan_fiber( true )
109 #endif
110  {}
111 
113  {
114 #ifdef KICAD_SANITIZE_THREADS
115  // Only destroy the fiber when we own it
116  if( own_tsan_fiber )
117  __tsan_destroy_fiber( tsan_fiber );
118 #endif
119  }
120  };
121 
123  {
124  public:
126  m_mainStackContext( nullptr )
127  {
128  }
129 
131  {
132  if( m_mainStackContext )
133  libcontext::release_fcontext( m_mainStackContext->ctx );
134  }
135 
136 
137  void SetMainStack( CONTEXT_T* aStack )
138  {
139  m_mainStackContext = aStack;
140  }
141 
142  void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
143  {
144  m_mainStackFunction = std::move( aFunc );
146 
147 #ifdef KICAD_SANITIZE_THREADS
148  // Tell TSAN we are changing fibers
149  __tsan_switch_to_fiber( m_mainStackContext->tsan_fiber, 0 );
150 #endif
151 
152  libcontext::jump_fcontext( &( aCor->m_callee.ctx ), m_mainStackContext->ctx,
153  reinterpret_cast<intptr_t>( &args ) );
154  }
155 
156  void Continue( INVOCATION_ARGS* args )
157  {
159  {
162  args = args->destination->doResume( args );
163  }
164  }
165 
166  private:
168  std::function<void()> m_mainStackFunction;
169  };
170 
171 public:
173  COROUTINE( nullptr )
174  {
175  }
176 
180  template <class T>
181  COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
182  COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
183  {
184  }
185 
189  COROUTINE( std::function<ReturnType(ArgType)> aEntry ) :
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  }
206 
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  }
219 
220 public:
227  void KiYield()
228  {
229  jumpOut();
230  }
231 
237  void KiYield( ReturnType& aRetVal )
238  {
239  m_retVal = aRetVal;
240  jumpOut();
241  }
242 
250  void RunMainStack( std::function<void()> func )
251  {
252  assert( m_callContext );
253  m_callContext->RunMainStack( this, std::move( func ) );
254  }
255 
264  bool Call( ArgType aArg )
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  }
281 
290  bool Call( const COROUTINE& aCor, ArgType aArg )
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  }
301 
310  bool Resume()
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  }
327 
336  bool Resume( const COROUTINE& aCor )
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  }
347 
351  const ReturnType& ReturnValue() const
352  {
353  return m_retVal;
354  }
355 
359  bool Running() const
360  {
361  return m_running;
362  }
363 
364 private:
365  INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
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  }
409 
410  INVOCATION_ARGS* doResume( INVOCATION_ARGS* args )
411  {
412  return jumpIn( args );
413  }
414 
415  /* real entry point of the coroutine */
416  static void callerStub( intptr_t aData )
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  }
434 
435  INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
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  }
451 
452  void jumpOut()
453  {
454  INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
455  INVOCATION_ARGS* ret;
456 
457 #ifdef KICAD_SANITIZE_THREADS
458  // Tell TSAN we are changing fibers back to the caller
459  __tsan_switch_to_fiber( m_caller.tsan_fiber, 0 );
460 #endif
461 
462  wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::jumpOut" );
463 
464  ret = reinterpret_cast<INVOCATION_ARGS*>(
465  libcontext::jump_fcontext( &( m_callee.ctx ), m_caller.ctx,
466  reinterpret_cast<intptr_t>( &args ) )
467  );
468 
469  m_callContext = ret->context;
470 
471  if( ret->type == INVOCATION_ARGS::FROM_ROOT )
472  {
474  }
475  }
476 
478  std::unique_ptr<char[]> m_stack;
479 
481 
482  std::function<ReturnType( ArgType )> m_func;
483 
484  bool m_running;
485 
488  typename std::remove_reference<ArgType>::type* m_args;
489 
491  CONTEXT_T m_caller;
492 
494  CALL_CONTEXT* m_callContext;
495 
497  CONTEXT_T m_callee;
498 
499  ReturnType m_retVal;
500 
501 #ifdef KICAD_USE_VALGRIND
502  uint32_t m_valgrind_stack;
503 #endif
504 #ifdef KICAD_SANITIZE_ADDRESS
505  void* asan_stack;
506 #endif
507 };
508 
509 #endif
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
Definition: coroutine.h:250
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:482
void KiYield(ReturnType &aRetVal)
KiYield with a value.
Definition: coroutine.h:237
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484
Implement a coroutine.
Definition: coroutine.h:75
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:142
void jumpOut()
coroutine stack
Definition: coroutine.h:452
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:264
bool Resume(const COROUTINE &aCor)
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:336
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:167
std::unique_ptr< char[]> m_stack
Definition: coroutine.h:478
bool Call(const COROUTINE &aCor, ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:290
Definition: bitmap.cpp:64
std::function< void()> m_mainStackFunction
Definition: coroutine.h:168
libcontext::fcontext_t ctx
Definition: coroutine.h:98
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
wxLogTrace helper definitions.
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:365
bool Resume()
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:310
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:488
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:137
const ReturnType & ReturnValue() const
Return the yielded value (the argument KiYield() was called with).
Definition: coroutine.h:351
ReturnType m_retVal
Definition: coroutine.h:499
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:410
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
int m_CoroutineStackSize
Set the stack size for coroutines.
~COROUTINE()
Definition: coroutine.h:207
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:156
static void callerStub(intptr_t aData)
Definition: coroutine.h:416
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:435
bool Running() const
Definition: coroutine.h:359
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CALL_CONTEXT * context
Definition: coroutine.h:92
enum COROUTINE::INVOCATION_ARGS::@32 type
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Create a coroutine from a delegate object.
Definition: coroutine.h:189
void KiYield()
Stop execution of the coroutine and returns control to the caller.
Definition: coroutine.h:227
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Create a coroutine from a member method of an object.
Definition: coroutine.h:181