fiber.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/assert.h"
  5. #include "common/fiber.h"
  6. #include "common/spin_lock.h"
  7. #include "common/virtual_buffer.h"
  8. #include <boost/context/detail/fcontext.hpp>
  9. namespace Common {
  10. constexpr std::size_t default_stack_size = 256 * 1024;
  11. struct Fiber::FiberImpl {
  12. FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
  13. VirtualBuffer<u8> stack;
  14. VirtualBuffer<u8> rewind_stack;
  15. SpinLock guard{};
  16. std::function<void(void*)> entry_point;
  17. std::function<void(void*)> rewind_point;
  18. void* rewind_parameter{};
  19. void* start_parameter{};
  20. Fiber* previous_fiber;
  21. bool is_thread_fiber{};
  22. bool released{};
  23. u8* stack_limit{};
  24. u8* rewind_stack_limit{};
  25. boost::context::detail::fcontext_t context{};
  26. boost::context::detail::fcontext_t rewind_context{};
  27. };
  28. void Fiber::SetStartParameter(void* new_parameter) {
  29. impl->start_parameter = new_parameter;
  30. }
  31. void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
  32. impl->rewind_point = std::move(rewind_func);
  33. impl->rewind_parameter = rewind_param;
  34. }
  35. void Fiber::Start(boost::context::detail::transfer_t& transfer) {
  36. ASSERT(impl->previous_fiber != nullptr);
  37. impl->previous_fiber->impl->context = transfer.fctx;
  38. impl->previous_fiber->impl->guard.unlock();
  39. impl->previous_fiber = nullptr;
  40. impl->entry_point(impl->start_parameter);
  41. UNREACHABLE();
  42. }
  43. void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
  44. ASSERT(impl->context != nullptr);
  45. impl->context = impl->rewind_context;
  46. impl->rewind_context = nullptr;
  47. u8* tmp = impl->stack_limit;
  48. impl->stack_limit = impl->rewind_stack_limit;
  49. impl->rewind_stack_limit = tmp;
  50. impl->rewind_point(impl->rewind_parameter);
  51. UNREACHABLE();
  52. }
  53. void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
  54. auto* fiber = static_cast<Fiber*>(transfer.data);
  55. fiber->Start(transfer);
  56. }
  57. void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
  58. auto* fiber = static_cast<Fiber*>(transfer.data);
  59. fiber->OnRewind(transfer);
  60. }
  61. Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
  62. : impl{std::make_unique<FiberImpl>()} {
  63. impl->entry_point = std::move(entry_point_func);
  64. impl->start_parameter = start_parameter;
  65. impl->stack_limit = impl->stack.data();
  66. impl->rewind_stack_limit = impl->rewind_stack.data();
  67. u8* stack_base = impl->stack_limit + default_stack_size;
  68. impl->context =
  69. boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
  70. }
  71. Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
  72. Fiber::~Fiber() {
  73. if (impl->released) {
  74. return;
  75. }
  76. // Make sure the Fiber is not being used
  77. const bool locked = impl->guard.try_lock();
  78. ASSERT_MSG(locked, "Destroying a fiber that's still running");
  79. if (locked) {
  80. impl->guard.unlock();
  81. }
  82. }
  83. void Fiber::Exit() {
  84. ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
  85. if (!impl->is_thread_fiber) {
  86. return;
  87. }
  88. impl->guard.unlock();
  89. impl->released = true;
  90. }
  91. void Fiber::Rewind() {
  92. ASSERT(impl->rewind_point);
  93. ASSERT(impl->rewind_context == nullptr);
  94. u8* stack_base = impl->rewind_stack_limit + default_stack_size;
  95. impl->rewind_context =
  96. boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
  97. boost::context::detail::jump_fcontext(impl->rewind_context, this);
  98. }
  99. void Fiber::YieldTo(Fiber* from, Fiber* to) {
  100. ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
  101. ASSERT_MSG(to != nullptr, "Next fiber is null!");
  102. to->impl->guard.lock();
  103. to->impl->previous_fiber = from;
  104. auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to);
  105. ASSERT(from->impl->previous_fiber != nullptr);
  106. from->impl->previous_fiber->impl->context = transfer.fctx;
  107. from->impl->previous_fiber->impl->guard.unlock();
  108. from->impl->previous_fiber = nullptr;
  109. }
  110. std::unique_ptr<Fiber> Fiber::ThreadToFiber() {
  111. std::unique_ptr<Fiber> fiber = std::unique_ptr<Fiber>{new Fiber()};
  112. fiber->impl->guard.lock();
  113. fiber->impl->is_thread_fiber = true;
  114. return fiber;
  115. }
  116. } // namespace Common