34#ifdef KICAD_USE_VALGRIND
35#include <valgrind/valgrind.h>
37#ifdef KICAD_SANITIZE_THREADS
38#include <sanitizer/tsan_interface.h>
40#ifdef KICAD_SANITIZE_ADDRESS
41#include <sanitizer/asan_interface.h>
44#include <libcontext.h>
82template <
typename ReturnType,
typename ArgType>
107 libcontext::fcontext_t
ctx;
109#ifdef KICAD_SANITIZE_THREADS
116#ifdef KICAD_SANITIZE_THREADS
117 ,tsan_fiber( nullptr )
118 ,own_tsan_fiber( true )
124#ifdef KICAD_SANITIZE_THREADS
127 __tsan_destroy_fiber( tsan_fiber );
157#ifdef KICAD_SANITIZE_THREADS
162#ifdef KICAD_SANITIZE_ADDRESS
163 void* fake_stack_save =
nullptr;
164 __sanitizer_start_switch_fiber( &fake_stack_save,
nullptr, 0 );
168 reinterpret_cast<intptr_t
>( &args ) );
170#ifdef KICAD_SANITIZE_ADDRESS
171 __sanitizer_finish_switch_fiber( fake_stack_save,
nullptr,
nullptr );
208 COROUTINE( std::function<ReturnType( ArgType )> aEntry ) :
216#ifdef KICAD_USE_VALGRIND
217 ,m_valgrind_stack( 0 )
219#ifdef KICAD_SANITIZE_ADDRESS
220 ,m_asanStackBottom( nullptr )
221 ,m_asanStackSize( 0 )
222 ,m_asanCallerStackBottom( nullptr )
223 ,m_asanCallerStackSize( 0 )
231#ifdef KICAD_USE_VALGRIND
232 VALGRIND_STACK_DEREGISTER( m_valgrind_stack );
236 libcontext::release_fcontext(
m_caller.ctx );
239 libcontext::release_fcontext(
m_callee.ctx );
291#ifdef KICAD_SANITIZE_THREADS
293 m_caller.tsan_fiber = __tsan_get_current_fiber();
299 ctx.Continue(
doCall( &args, aArg ) );
337#ifdef KICAD_SANITIZE_THREADS
339 m_caller.tsan_fiber = __tsan_get_current_fiber();
387 INVOCATION_ARGS*
doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
399#ifndef LIBCONTEXT_HAS_OWN_STACK
405 std::size_t pages = (
m_stacksize + systemPageSize - 1 ) / systemPageSize;
408 stackSize = ( pages + 1 ) * systemPageSize;
411 m_stack.get_deleter().SetSize( stackSize );
418 sp =
static_cast<char*
>(
m_stack.get() ) + stackSize;
420#ifdef KICAD_USE_VALGRIND
421 m_valgrind_stack = VALGRIND_STACK_REGISTER( sp,
m_stack.get() );
425#ifdef KICAD_SANITIZE_THREADS
427 m_callee.tsan_fiber = __tsan_create_fiber( 0 );
430 __tsan_set_fiber_name(
m_callee.tsan_fiber,
"Coroutine fiber" );
433#if defined( KICAD_SANITIZE_ADDRESS ) && !defined( LIBCONTEXT_HAS_OWN_STACK )
434 m_asanStackBottom =
m_stack.get();
435 m_asanStackSize = stackSize;
442 return jumpIn( aInvArgs );
445#ifndef LIBCONTEXT_HAS_OWN_STACK
451 void operator()(
void* aMem )
noexcept { ::VirtualFree( aMem, 0, MEM_RELEASE ); }
463 static std::optional<size_t> systemPageSize;
465 if( !systemPageSize.has_value() )
469 ::GetSystemInfo( &si );
470 systemPageSize =
static_cast<size_t>( si.dwPageSize );
472 int size = getpagesize();
473 systemPageSize =
static_cast<size_t>( size );
477 return systemPageSize.value();
484 void* mem = ::VirtualAlloc( 0, aAllocSize, MEM_COMMIT, PAGE_READWRITE );
487 throw std::bad_alloc();
489 void* mem = ::mmap( 0, aAllocSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 );
491 if( mem == (
void*) -1 )
492 throw std::bad_alloc();
498 static inline void GuardMemory(
void* aAddress,
size_t aGuardSize )
502 BOOL
res = ::VirtualProtect( aAddress, aGuardSize,
503 PAGE_READWRITE | PAGE_GUARD, &old_prot );
505 bool res = ( 0 == ::mprotect( aAddress, aGuardSize, PROT_NONE ) );
520#ifdef KICAD_SANITIZE_ADDRESS
521 const void* caller_bottom =
nullptr;
522 size_t caller_size = 0;
523 __sanitizer_finish_switch_fiber(
nullptr, &caller_bottom, &caller_size );
526 INVOCATION_ARGS& args = *
reinterpret_cast<INVOCATION_ARGS*
>( aData );
531#ifdef KICAD_SANITIZE_ADDRESS
532 cor->m_asanCallerStackBottom = caller_bottom;
533 cor->m_asanCallerStackSize = caller_size;
548 INVOCATION_ARGS*
jumpIn( INVOCATION_ARGS* args )
550#ifdef KICAD_SANITIZE_THREADS
552 __tsan_switch_to_fiber(
m_callee.tsan_fiber, 0 );
555#ifdef KICAD_SANITIZE_ADDRESS
556 void* fake_stack_save =
nullptr;
557 __sanitizer_start_switch_fiber( &fake_stack_save, m_asanStackBottom, m_asanStackSize );
562 args =
reinterpret_cast<INVOCATION_ARGS*
>(
564 reinterpret_cast<intptr_t
>( args ) )
567#ifdef KICAD_SANITIZE_ADDRESS
568 __sanitizer_finish_switch_fiber( fake_stack_save,
nullptr,
nullptr );
577 INVOCATION_ARGS* ret;
579#ifdef KICAD_SANITIZE_THREADS
581 __tsan_switch_to_fiber(
m_caller.tsan_fiber, 0 );
584#ifdef KICAD_SANITIZE_ADDRESS
585 void* fake_stack_save =
nullptr;
586 __sanitizer_start_switch_fiber( &fake_stack_save,
587 m_asanCallerStackBottom, m_asanCallerStackSize );
592 ret =
reinterpret_cast<INVOCATION_ARGS*
>(
594 reinterpret_cast<intptr_t
>( &args ) )
597#ifdef KICAD_SANITIZE_ADDRESS
598 __sanitizer_finish_switch_fiber( fake_stack_save,
599 &m_asanCallerStackBottom, &m_asanCallerStackSize );
610#ifndef LIBCONTEXT_HAS_OWN_STACK
612 std::unique_ptr<char[], struct STACK_DELETER>
m_stack;
617 std::function<ReturnType( ArgType )>
m_func;
622 typename std::remove_reference<ArgType>::type*
m_args;
635#ifdef KICAD_USE_VALGRIND
636 uint32_t m_valgrind_stack;
639#ifdef KICAD_SANITIZE_ADDRESS
640 const void* m_asanStackBottom;
641 size_t m_asanStackSize;
642 const void* m_asanCallerStackBottom;
643 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.