You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

590 lines
17 KiB

13 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
13 years ago
13 years ago
5 years ago
  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. #ifndef __COROUTINE_H
  27. #define __COROUTINE_H
  28. #include <cassert>
  29. #include <cstdlib>
  30. #include <type_traits>
  31. #ifdef KICAD_USE_VALGRIND
  32. #include <valgrind/valgrind.h>
  33. #endif
  34. #ifdef KICAD_SANITIZE_THREADS
  35. #include <sanitizer/tsan_interface.h>
  36. #endif
  37. #ifdef KICAD_SANITIZE_ADDRESS
  38. #include <sanitizer/asan_interface.h>
  39. #endif
  40. #include <libcontext.h>
  41. #include <functional>
  42. #include <optional>
  43. #include <memory>
  44. #include <advanced_config.h>
  45. #include <trace_helpers.h>
  46. #include <wx/log.h>
  47. #ifdef _WIN32
  48. #include <windows.h>
  49. #else // Linux, BSD, MacOS
  50. #include <unistd.h> // getpagesize
  51. #include <sys/mman.h> // mmap, mprotect, munmap
  52. #endif
  53. /**
  54. * Implement a coroutine.
  55. *
  56. * Wikipedia has a good explanation:
  57. *
  58. * "Coroutines are computer program components that generalize subroutines to
  59. * allow multiple entry points for suspending and resuming execution at certain locations.
  60. * Coroutines are well-suited for implementing more familiar program components such as cooperative
  61. * tasks, exceptions, event loop, iterators, infinite lists and pipes."
  62. *
  63. * In other words, a coroutine can be considered a lightweight thread - which can be
  64. * preempted only when it deliberately yields the control to the caller. This way,
  65. * we avoid concurrency problems such as locking / race conditions.
  66. *
  67. * Uses libcontext library to do the actual context switching.
  68. *
  69. * This particular version takes a DELEGATE as an entry point, so it can invoke
  70. * methods within a given object as separate coroutines.
  71. *
  72. * See coroutine_example.cpp for sample code.
  73. */
  74. template <typename ReturnType, typename ArgType>
  75. class COROUTINE
  76. {
  77. private:
  78. class CALL_CONTEXT;
  79. struct INVOCATION_ARGS
  80. {
  81. enum
  82. {
  83. FROM_ROOT, // a stub was called/a coroutine was resumed from the main-stack context
  84. FROM_ROUTINE, // a stub was called/a coroutine was resumed from a coroutine context
  85. CONTINUE_AFTER_ROOT // a function sent a request to invoke a function on the main
  86. // stack context
  87. } type; // invocation type
  88. COROUTINE* destination; // stores the coroutine pointer for the stub OR the coroutine
  89. // ptr for the coroutine to be resumed if a
  90. // root(main-stack)-call-was initiated.
  91. CALL_CONTEXT* context; // pointer to the call context of the current callgraph this
  92. // call context holds a reference to the main stack context
  93. };
  94. struct CONTEXT_T
  95. {
  96. libcontext::fcontext_t ctx; // The context itself
  97. #ifdef KICAD_SANITIZE_THREADS
  98. void* tsan_fiber; // The TSAN fiber for this context
  99. bool own_tsan_fiber; // Do we own this TSAN fiber? (we only delete fibers we own)
  100. #endif
  101. CONTEXT_T() :
  102. ctx( nullptr )
  103. #ifdef KICAD_SANITIZE_THREADS
  104. ,tsan_fiber( nullptr )
  105. ,own_tsan_fiber( true )
  106. #endif
  107. {}
  108. ~CONTEXT_T()
  109. {
  110. #ifdef KICAD_SANITIZE_THREADS
  111. // Only destroy the fiber when we own it
  112. if( own_tsan_fiber )
  113. __tsan_destroy_fiber( tsan_fiber );
  114. #endif
  115. }
  116. };
  117. class CALL_CONTEXT
  118. {
  119. public:
  120. CALL_CONTEXT() :
  121. m_mainStackContext( nullptr )
  122. {
  123. }
  124. ~CALL_CONTEXT()
  125. {
  126. if( m_mainStackContext )
  127. libcontext::release_fcontext( m_mainStackContext->ctx );
  128. }
  129. void SetMainStack( CONTEXT_T* aStack )
  130. {
  131. m_mainStackContext = aStack;
  132. }
  133. void RunMainStack( COROUTINE* aCor, std::function<void()> aFunc )
  134. {
  135. m_mainStackFunction = std::move( aFunc );
  136. INVOCATION_ARGS args{ INVOCATION_ARGS::CONTINUE_AFTER_ROOT, aCor, this };
  137. #ifdef KICAD_SANITIZE_THREADS
  138. // Tell TSAN we are changing fibers
  139. __tsan_switch_to_fiber( m_mainStackContext->tsan_fiber, 0 );
  140. #endif
  141. libcontext::jump_fcontext( &( aCor->m_callee.ctx ), m_mainStackContext->ctx,
  142. reinterpret_cast<intptr_t>( &args ) );
  143. }
  144. void Continue( INVOCATION_ARGS* args )
  145. {
  146. while( args->type == INVOCATION_ARGS::CONTINUE_AFTER_ROOT )
  147. {
  148. m_mainStackFunction();
  149. args->type = INVOCATION_ARGS::FROM_ROOT;
  150. args = args->destination->doResume( args );
  151. }
  152. }
  153. private:
  154. CONTEXT_T* m_mainStackContext;
  155. std::function<void()> m_mainStackFunction;
  156. };
  157. public:
  158. COROUTINE() :
  159. COROUTINE( nullptr )
  160. {
  161. }
  162. /**
  163. * Create a coroutine from a member method of an object.
  164. */
  165. template <class T>
  166. COROUTINE( T* object, ReturnType(T::*ptr)( ArgType ) ) :
  167. COROUTINE( std::bind( ptr, object, std::placeholders::_1 ) )
  168. {
  169. }
  170. /**
  171. * Create a coroutine from a delegate object.
  172. */
  173. COROUTINE( std::function<ReturnType( ArgType )> aEntry ) :
  174. m_func( std::move( aEntry ) ),
  175. m_running( false ),
  176. m_args( nullptr ),
  177. m_caller(),
  178. m_callContext( nullptr ),
  179. m_callee(),
  180. m_retVal( 0 )
  181. #ifdef KICAD_USE_VALGRIND
  182. ,m_valgrind_stack( 0 )
  183. #endif
  184. #ifdef KICAD_SANITIZE_ADDRESS
  185. ,asan_stack( nullptr )
  186. #endif
  187. {
  188. m_stacksize = ADVANCED_CFG::GetCfg().m_CoroutineStackSize;
  189. }
  190. ~COROUTINE()
  191. {
  192. #ifdef KICAD_USE_VALGRIND
  193. VALGRIND_STACK_DEREGISTER( m_valgrind_stack );
  194. #endif
  195. if( m_caller.ctx )
  196. libcontext::release_fcontext( m_caller.ctx );
  197. if( m_callee.ctx )
  198. libcontext::release_fcontext( m_callee.ctx );
  199. }
  200. public:
  201. /**
  202. * Stop execution of the coroutine and returns control to the caller.
  203. *
  204. * After a yield, Call() or Resume() methods invoked by the caller will
  205. * immediately return true, indicating that we are not done yet, just asleep.
  206. */
  207. void KiYield()
  208. {
  209. jumpOut();
  210. }
  211. /**
  212. * KiYield with a value.
  213. *
  214. * Passe a value of given type to the caller. Useful for implementing generator objects.
  215. */
  216. void KiYield( ReturnType& aRetVal )
  217. {
  218. m_retVal = aRetVal;
  219. jumpOut();
  220. }
  221. /**
  222. * Run a functor inside the application main stack context.
  223. *
  224. * Call this function for example if the operation will spawn a webkit browser instance which
  225. * will walk the stack to the upper border of the address space on mac osx systems because
  226. * its javascript needs garbage collection (for example if you paste text into an edit box).
  227. */
  228. void RunMainStack( std::function<void()> func )
  229. {
  230. assert( m_callContext );
  231. m_callContext->RunMainStack( this, std::move( func ) );
  232. }
  233. /**
  234. * Start execution of a coroutine, passing args as its arguments.
  235. *
  236. * Call this method from the application main stack only.
  237. *
  238. * @return true if the coroutine has yielded and false if it has finished its
  239. * execution (returned).
  240. */
  241. bool Call( ArgType aArg )
  242. {
  243. CALL_CONTEXT ctx;
  244. INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
  245. #ifdef KICAD_SANITIZE_THREADS
  246. // Get the TSAN fiber for the current stack here
  247. m_caller.tsan_fiber = __tsan_get_current_fiber();
  248. m_caller.own_tsan_fiber = false;
  249. #endif
  250. wxLogTrace( kicadTraceCoroutineStack, "COROUTINE::Call (from root)" );
  251. ctx.Continue( doCall( &args, aArg ) );
  252. return Running();
  253. }
  254. /**
  255. * Start execution of a coroutine, passing args as its arguments.
  256. *
  257. * Call this method for a nested coroutine invocation.
  258. *
  259. * @return true if the coroutine has yielded and false if it has finished its
  260. * execution (returned).
  261. */
  262. bool Call( const COROUTINE& aCor, ArgType aArg )
  263. {
  264. INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
  265. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::Call (from routine)" ) );
  266. doCall( &args, aArg );
  267. // we will not be asked to continue
  268. return Running();
  269. }
  270. /**
  271. * Resume execution of a previously yielded coroutine.
  272. *
  273. * Call this method only from the main application stack.
  274. *
  275. * @return true if the coroutine has yielded again and false if it has finished its
  276. * execution (returned).
  277. */
  278. bool Resume()
  279. {
  280. CALL_CONTEXT ctx;
  281. INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROOT, this, &ctx };
  282. #ifdef KICAD_SANITIZE_THREADS
  283. // Get the TSAN fiber for the current stack here
  284. m_caller.tsan_fiber = __tsan_get_current_fiber();
  285. m_caller.own_tsan_fiber = false;
  286. #endif
  287. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::Resume (from root)" ) );
  288. ctx.Continue( doResume( &args ) );
  289. return Running();
  290. }
  291. /**
  292. * Resume execution of a previously yielded coroutine.
  293. *
  294. * Call this method for a nested coroutine invocation.
  295. *
  296. * @return true if the coroutine has yielded again and false if it has finished its
  297. * execution (returned).
  298. */
  299. bool Resume( const COROUTINE& aCor )
  300. {
  301. INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, this, aCor.m_callContext };
  302. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::Resume (from routine)" ) );
  303. doResume( &args );
  304. // we will not be asked to continue
  305. return Running();
  306. }
  307. /**
  308. * Return the yielded value (the argument KiYield() was called with).
  309. */
  310. const ReturnType& ReturnValue() const
  311. {
  312. return m_retVal;
  313. }
  314. /**
  315. * @return true if the coroutine is active.
  316. */
  317. bool Running() const
  318. {
  319. return m_running;
  320. }
  321. private:
  322. INVOCATION_ARGS* doCall( INVOCATION_ARGS* aInvArgs, ArgType aArgs )
  323. {
  324. assert( m_func );
  325. assert( !( m_callee.ctx ) );
  326. m_args = &aArgs;
  327. std::size_t stackSize = m_stacksize;
  328. void* sp = nullptr;
  329. #ifndef LIBCONTEXT_HAS_OWN_STACK
  330. assert( !m_stack );
  331. const std::size_t systemPageSize = SystemPageSize();
  332. // calculate the correct number of pages to allocate based on request stack size
  333. std::size_t pages = ( m_stacksize + systemPageSize - 1 ) / systemPageSize;
  334. // we allocate an extra page for the guard
  335. stackSize = ( pages + 1 ) * systemPageSize;
  336. m_stack.reset( static_cast<char*>( MapMemory( stackSize ) ) );
  337. m_stack.get_deleter().SetSize( stackSize );
  338. // now configure the first page (by only specifying a single page_size from vp)
  339. // that will act as the guard page
  340. // the stack will grow from the end and hopefully never into this guarded region
  341. GuardMemory( m_stack.get(), systemPageSize );
  342. sp = static_cast<char*>( m_stack.get() ) + stackSize;
  343. #ifdef KICAD_USE_VALGRIND
  344. m_valgrind_stack = VALGRIND_STACK_REGISTER( sp, m_stack.get() );
  345. #endif
  346. #endif
  347. #ifdef KICAD_SANITIZE_THREADS
  348. // Create a new fiber to go with the new context
  349. m_callee.tsan_fiber = __tsan_create_fiber( 0 );
  350. m_callee.own_tsan_fiber = true;
  351. __tsan_set_fiber_name( m_callee.tsan_fiber, "Coroutine fiber" );
  352. #endif
  353. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::doCall" ) );
  354. m_callee.ctx = libcontext::make_fcontext( sp, stackSize, callerStub );
  355. m_running = true;
  356. // off we go!
  357. return jumpIn( aInvArgs );
  358. }
  359. #ifndef LIBCONTEXT_HAS_OWN_STACK
  360. ///< A functor that frees the stack
  361. struct STACK_DELETER
  362. {
  363. #ifdef _WIN32
  364. void SetSize( std::size_t ) {}
  365. void operator()( void* aMem ) noexcept { ::VirtualFree( aMem, 0, MEM_RELEASE ); }
  366. #else
  367. std::size_t m_size = 0;
  368. void SetSize( std::size_t aSize ) { m_size = aSize; }
  369. void operator()( void* aMem ) noexcept { ::munmap( aMem, m_size ); }
  370. #endif
  371. };
  372. ///< The size of the mappable memory page size
  373. static inline size_t SystemPageSize()
  374. {
  375. static std::optional<size_t> systemPageSize;
  376. if( !systemPageSize.has_value() )
  377. {
  378. #ifdef _WIN32
  379. SYSTEM_INFO si;
  380. ::GetSystemInfo( &si );
  381. systemPageSize = static_cast<size_t>( si.dwPageSize );
  382. #else
  383. int size = getpagesize();
  384. systemPageSize = static_cast<size_t>( size );
  385. #endif
  386. }
  387. return systemPageSize.value();
  388. }
  389. ///< Map a page-aligned memory region into our address space.
  390. static inline void* MapMemory( size_t aAllocSize )
  391. {
  392. #ifdef _WIN32
  393. void* mem = ::VirtualAlloc( 0, aAllocSize, MEM_COMMIT, PAGE_READWRITE );
  394. if( !mem )
  395. throw std::bad_alloc();
  396. #else
  397. void* mem = ::mmap( 0, aAllocSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0 );
  398. if( mem == (void*) -1 )
  399. throw std::bad_alloc();
  400. #endif
  401. return mem;
  402. }
  403. ///< Change protection of memory page(s) to act as stack guards.
  404. static inline void GuardMemory( void* aAddress, size_t aGuardSize )
  405. {
  406. #ifdef _WIN32
  407. DWORD old_prot; // dummy var since the arg cannot be NULL
  408. BOOL res = ::VirtualProtect( aAddress, aGuardSize,
  409. PAGE_READWRITE | PAGE_GUARD, &old_prot );
  410. #else
  411. bool res = ( 0 == ::mprotect( aAddress, aGuardSize, PROT_NONE ) );
  412. #endif
  413. if( !res )
  414. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::GuardMemory has failes" ) );
  415. }
  416. #endif // LIBCONTEXT_HAS_OWN_STACK
  417. INVOCATION_ARGS* doResume( INVOCATION_ARGS* args )
  418. {
  419. return jumpIn( args );
  420. }
  421. /* real entry point of the coroutine */
  422. static void callerStub( intptr_t aData )
  423. {
  424. INVOCATION_ARGS& args = *reinterpret_cast<INVOCATION_ARGS*>( aData );
  425. // get pointer to self
  426. COROUTINE* cor = args.destination;
  427. cor->m_callContext = args.context;
  428. if( args.type == INVOCATION_ARGS::FROM_ROOT )
  429. cor->m_callContext->SetMainStack( &cor->m_caller );
  430. // call the coroutine method
  431. cor->m_retVal = cor->m_func( *(cor->m_args) );
  432. cor->m_running = false;
  433. // go back to wherever we came from.
  434. cor->jumpOut();
  435. }
  436. INVOCATION_ARGS* jumpIn( INVOCATION_ARGS* args )
  437. {
  438. #ifdef KICAD_SANITIZE_THREADS
  439. // Tell TSAN we are changing fibers to the callee
  440. __tsan_switch_to_fiber( m_callee.tsan_fiber, 0 );
  441. #endif
  442. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::jumpIn" ) );
  443. args = reinterpret_cast<INVOCATION_ARGS*>(
  444. libcontext::jump_fcontext( &( m_caller.ctx ), m_callee.ctx,
  445. reinterpret_cast<intptr_t>( args ) )
  446. );
  447. return args;
  448. }
  449. void jumpOut()
  450. {
  451. INVOCATION_ARGS args{ INVOCATION_ARGS::FROM_ROUTINE, nullptr, nullptr };
  452. INVOCATION_ARGS* ret;
  453. #ifdef KICAD_SANITIZE_THREADS
  454. // Tell TSAN we are changing fibers back to the caller
  455. __tsan_switch_to_fiber( m_caller.tsan_fiber, 0 );
  456. #endif
  457. wxLogTrace( kicadTraceCoroutineStack, wxT( "COROUTINE::jumpOut" ) );
  458. ret = reinterpret_cast<INVOCATION_ARGS*>(
  459. libcontext::jump_fcontext( &( m_callee.ctx ), m_caller.ctx,
  460. reinterpret_cast<intptr_t>( &args ) )
  461. );
  462. m_callContext = ret->context;
  463. if( ret->type == INVOCATION_ARGS::FROM_ROOT )
  464. {
  465. m_callContext->SetMainStack( &m_caller );
  466. }
  467. }
  468. #ifndef LIBCONTEXT_HAS_OWN_STACK
  469. ///< coroutine stack
  470. std::unique_ptr<char[], struct STACK_DELETER> m_stack;
  471. #endif
  472. int m_stacksize;
  473. std::function<ReturnType( ArgType )> m_func;
  474. bool m_running;
  475. ///< pointer to coroutine entry arguments. Stripped of references
  476. ///< to avoid compiler errors.
  477. typename std::remove_reference<ArgType>::type* m_args;
  478. ///< saved caller context
  479. CONTEXT_T m_caller;
  480. ///< main stack information
  481. CALL_CONTEXT* m_callContext;
  482. ///< saved coroutine context
  483. CONTEXT_T m_callee;
  484. ReturnType m_retVal;
  485. #ifdef KICAD_USE_VALGRIND
  486. uint32_t m_valgrind_stack;
  487. #endif
  488. #ifdef KICAD_SANITIZE_ADDRESS
  489. void* asan_stack;
  490. #endif
  491. };
  492. #endif