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 <[email protected]>
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
74template <typename ReturnType, typename ArgType>
76{
77private:
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 {
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
157 {
159 {
162 args = args->destination->doResume( args );
163 }
164 }
165
166 private:
168 std::function<void()> m_mainStackFunction;
169 };
170
171public:
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
220public:
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;
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 {
293
294 wxLogTrace( kicadTraceCoroutineStack, wxT( "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;
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, wxT( "COROUTINE::Resume (from root)" ) );
322
323 ctx.Continue( doResume( &args ) );
324
325 return Running();
326 }
327
336 bool Resume( const COROUTINE& aCor )
337 {
339
340 wxLogTrace( kicadTraceCoroutineStack, wxT( "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
364private:
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, wxT( "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
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
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, wxT( "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, wxT( "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
485
488 typename std::remove_reference<ArgType>::type* m_args;
489
492
495
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
int m_CoroutineStackSize
Set the stack size for coroutines.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CONTEXT_T * m_mainStackContext
Definition: coroutine.h:167
void SetMainStack(CONTEXT_T *aStack)
Definition: coroutine.h:137
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
Definition: coroutine.h:142
void Continue(INVOCATION_ARGS *args)
Definition: coroutine.h:156
std::function< void()> m_mainStackFunction
Definition: coroutine.h:168
Implement a coroutine.
Definition: coroutine.h:76
std::unique_ptr< char[]> m_stack
Definition: coroutine.h:478
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:264
CALL_CONTEXT * m_callContext
saved coroutine context
Definition: coroutine.h:494
void KiYield(ReturnType &aRetVal)
KiYield with a value.
Definition: coroutine.h:237
void KiYield()
Stop execution of the coroutine and returns control to the caller.
Definition: coroutine.h:227
CONTEXT_T m_caller
main stack information
Definition: coroutine.h:491
bool Resume()
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:310
ReturnType m_retVal
Definition: coroutine.h:499
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Create a coroutine from a delegate object.
Definition: coroutine.h:189
static void callerStub(intptr_t aData)
Definition: coroutine.h:416
~COROUTINE()
Definition: coroutine.h:207
CONTEXT_T m_callee
Definition: coroutine.h:497
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
Definition: coroutine.h:435
const ReturnType & ReturnValue() const
Return the yielded value (the argument KiYield() was called with).
Definition: coroutine.h:351
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
Definition: coroutine.h:410
bool m_running
pointer to coroutine entry arguments.
Definition: coroutine.h:484
int m_stacksize
Definition: coroutine.h:480
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
Definition: coroutine.h:365
bool Call(const COROUTINE &aCor, ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
Definition: coroutine.h:290
std::function< ReturnType(ArgType)> m_func
Definition: coroutine.h:482
bool Resume(const COROUTINE &aCor)
Resume execution of a previously yielded coroutine.
Definition: coroutine.h:336
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Create a coroutine from a member method of an object.
Definition: coroutine.h:181
bool Running() const
Definition: coroutine.h:359
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
Definition: coroutine.h:250
void jumpOut()
coroutine stack
Definition: coroutine.h:452
std::remove_reference< ArgType >::type * m_args
saved caller context
Definition: coroutine.h:488
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
Definition: bitmap.cpp:64
libcontext::fcontext_t ctx
Definition: coroutine.h:98
enum COROUTINE::INVOCATION_ARGS::@36 type
CALL_CONTEXT * context
Definition: coroutine.h:92
wxLogTrace helper definitions.