30#ifdef KICAD_USE_VALGRIND
31#include <valgrind/valgrind.h>
33#ifdef KICAD_SANITIZE_THREADS
34#include <sanitizer/tsan_interface.h>
36#ifdef KICAD_SANITIZE_ADDRESS
37#include <sanitizer/asan_interface.h>
40#include <libcontext.h>
78template <
typename ReturnType,
typename ArgType>
103 libcontext::fcontext_t
ctx;
105#ifdef KICAD_SANITIZE_THREADS
112#ifdef KICAD_SANITIZE_THREADS
113 ,tsan_fiber( nullptr )
114 ,own_tsan_fiber( true )
120#ifdef KICAD_SANITIZE_THREADS
123 __tsan_destroy_fiber( tsan_fiber );
153#ifdef KICAD_SANITIZE_THREADS
158#ifdef KICAD_SANITIZE_ADDRESS
159 void* fake_stack_save =
nullptr;
160 __sanitizer_start_switch_fiber( &fake_stack_save,
nullptr, 0 );
164 reinterpret_cast<intptr_t
>( &args ) );
166#ifdef KICAD_SANITIZE_ADDRESS
167 __sanitizer_finish_switch_fiber( fake_stack_save,
nullptr,
nullptr );
204 COROUTINE( std::function<ReturnType( ArgType )> aEntry ) :
212#ifdef KICAD_USE_VALGRIND
213 ,m_valgrind_stack( 0 )
215#ifdef KICAD_SANITIZE_ADDRESS
216 ,m_asanStackBottom( nullptr )
217 ,m_asanStackSize( 0 )
218 ,m_asanCallerStackBottom( nullptr )
219 ,m_asanCallerStackSize( 0 )
227#ifdef KICAD_USE_VALGRIND
228 VALGRIND_STACK_DEREGISTER( m_valgrind_stack );
232 libcontext::release_fcontext(
m_caller.ctx );
235 libcontext::release_fcontext(
m_callee.ctx );
287#ifdef KICAD_SANITIZE_THREADS
289 m_caller.tsan_fiber = __tsan_get_current_fiber();
295 ctx.Continue(
doCall( &args, aArg ) );
333#ifdef KICAD_SANITIZE_THREADS
335 m_caller.tsan_fiber = __tsan_get_current_fiber();
383 INVOCATION_ARGS*
doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
395#ifndef LIBCONTEXT_HAS_OWN_STACK
401 std::size_t pages = (
m_stacksize + systemPageSize - 1 ) / systemPageSize;
404 stackSize = ( pages + 1 ) * systemPageSize;
407 m_stack.get_deleter().SetSize( stackSize );
414 sp =
static_cast<char*
>(
m_stack.get() ) + stackSize;
416#ifdef KICAD_USE_VALGRIND
417 m_valgrind_stack = VALGRIND_STACK_REGISTER( sp,
m_stack.get() );
421#ifdef KICAD_SANITIZE_THREADS
423 m_callee.tsan_fiber = __tsan_create_fiber( 0 );
426 __tsan_set_fiber_name(
m_callee.tsan_fiber,
"Coroutine fiber" );
429#if defined( KICAD_SANITIZE_ADDRESS ) && !defined( LIBCONTEXT_HAS_OWN_STACK )
430 m_asanStackBottom =
m_stack.get();
431 m_asanStackSize = stackSize;
438 return jumpIn( aInvArgs );
441#ifndef LIBCONTEXT_HAS_OWN_STACK
447 void operator()(
void* aMem )
noexcept { ::VirtualFree( aMem, 0, MEM_RELEASE ); }
459 static std::optional<size_t> systemPageSize;
461 if( !systemPageSize.has_value() )
465 ::GetSystemInfo( &si );
466 systemPageSize =
static_cast<size_t>( si.dwPageSize );
468 int size = getpagesize();
469 systemPageSize =
static_cast<size_t>( size );
473 return systemPageSize.value();
480 void* mem = ::VirtualAlloc( 0, aAllocSize, MEM_COMMIT, PAGE_READWRITE );
483 throw std::bad_alloc();
485 void* mem = ::mmap( 0, aAllocSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 );
487 if( mem == (
void*) -1 )
488 throw std::bad_alloc();
494 static inline void GuardMemory(
void* aAddress,
size_t aGuardSize )
498 BOOL
res = ::VirtualProtect( aAddress, aGuardSize,
499 PAGE_READWRITE | PAGE_GUARD, &old_prot );
501 bool res = ( 0 == ::mprotect( aAddress, aGuardSize, PROT_NONE ) );
516#ifdef KICAD_SANITIZE_ADDRESS
517 const void* caller_bottom =
nullptr;
518 size_t caller_size = 0;
519 __sanitizer_finish_switch_fiber(
nullptr, &caller_bottom, &caller_size );
522 INVOCATION_ARGS& args = *
reinterpret_cast<INVOCATION_ARGS*
>( aData );
527#ifdef KICAD_SANITIZE_ADDRESS
528 cor->m_asanCallerStackBottom = caller_bottom;
529 cor->m_asanCallerStackSize = caller_size;
544 INVOCATION_ARGS*
jumpIn( INVOCATION_ARGS* args )
546#ifdef KICAD_SANITIZE_THREADS
548 __tsan_switch_to_fiber(
m_callee.tsan_fiber, 0 );
551#ifdef KICAD_SANITIZE_ADDRESS
552 void* fake_stack_save =
nullptr;
553 __sanitizer_start_switch_fiber( &fake_stack_save, m_asanStackBottom, m_asanStackSize );
558 args =
reinterpret_cast<INVOCATION_ARGS*
>(
560 reinterpret_cast<intptr_t
>( args ) )
563#ifdef KICAD_SANITIZE_ADDRESS
564 __sanitizer_finish_switch_fiber( fake_stack_save,
nullptr,
nullptr );
573 INVOCATION_ARGS* ret;
575#ifdef KICAD_SANITIZE_THREADS
577 __tsan_switch_to_fiber(
m_caller.tsan_fiber, 0 );
580#ifdef KICAD_SANITIZE_ADDRESS
581 void* fake_stack_save =
nullptr;
582 __sanitizer_start_switch_fiber( &fake_stack_save,
583 m_asanCallerStackBottom, m_asanCallerStackSize );
588 ret =
reinterpret_cast<INVOCATION_ARGS*
>(
590 reinterpret_cast<intptr_t
>( &args ) )
593#ifdef KICAD_SANITIZE_ADDRESS
594 __sanitizer_finish_switch_fiber( fake_stack_save,
595 &m_asanCallerStackBottom, &m_asanCallerStackSize );
606#ifndef LIBCONTEXT_HAS_OWN_STACK
608 std::unique_ptr<char[], struct STACK_DELETER>
m_stack;
613 std::function<ReturnType( ArgType )>
m_func;
618 typename std::remove_reference<ArgType>::type*
m_args;
631#ifdef KICAD_USE_VALGRIND
632 uint32_t m_valgrind_stack;
635#ifdef KICAD_SANITIZE_ADDRESS
636 const void* m_asanStackBottom;
637 size_t m_asanStackSize;
638 const void* m_asanCallerStackBottom;
639 size_t m_asanCallerStackSize;
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
CONTEXT_T * m_mainStackContext
void SetMainStack(CONTEXT_T *aStack)
void RunMainStack(COROUTINE *aCor, std::function< void()> aFunc)
void Continue(INVOCATION_ARGS *args)
std::function< void()> m_mainStackFunction
bool Call(ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
CALL_CONTEXT * m_callContext
void KiYield(ReturnType &aRetVal)
KiYield with a value.
void KiYield()
Stop execution of the coroutine and returns control to the caller.
bool Resume()
Resume execution of a previously yielded coroutine.
COROUTINE(std::function< ReturnType(ArgType)> aEntry)
Create a coroutine from a delegate object.
static void callerStub(intptr_t aData)
CONTEXT_T m_callee
Saved coroutine context.
INVOCATION_ARGS * jumpIn(INVOCATION_ARGS *args)
std::unique_ptr< char[], struct STACK_DELETER > m_stack
const ReturnType & ReturnValue() const
Return the yielded value (the argument KiYield() was called with).
INVOCATION_ARGS * doResume(INVOCATION_ARGS *args)
static void * MapMemory(size_t aAllocSize)
Map a page-aligned memory region into our address space.
static size_t SystemPageSize()
The size of the mappable memory page size.
INVOCATION_ARGS * doCall(INVOCATION_ARGS *aInvArgs, ArgType aArgs)
bool Call(const COROUTINE &aCor, ArgType aArg)
Start execution of a coroutine, passing args as its arguments.
std::function< int(int)> m_func
static void GuardMemory(void *aAddress, size_t aGuardSize)
Change protection of memory page(s) to act as stack guards.
bool Resume(const COROUTINE &aCor)
Resume execution of a previously yielded coroutine.
COROUTINE(T *object, ReturnType(T::*ptr)(ArgType))
Create a coroutine from a member method of an object.
void RunMainStack(std::function< void()> func)
Run a functor inside the application main stack context.
std::remove_reference< int >::type * m_args
int m_CoroutineStackSize
Configure the coroutine stack size in bytes.
const wxChar *const kicadTraceCoroutineStack
Flag to enable tracing of the coroutine call stack.
libcontext::fcontext_t ctx
enum COROUTINE::INVOCATION_ARGS::@361002175157160365344154346100200253026233123042 type
A functor that frees the stack.
void SetSize(std::size_t aSize)
void operator()(void *aMem) noexcept
wxLogTrace helper definitions.