fiber.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  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. #if defined(_WIN32) || defined(WIN32)
  8. #include <windows.h>
  9. #else
  10. #include <boost/context/detail/fcontext.hpp>
  11. #endif
  12. namespace Common {
  13. constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
  14. struct Fiber::FiberImpl {
  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. std::shared_ptr<Fiber> previous_fiber;
  21. bool is_thread_fiber{};
  22. bool released{};
  23. #if defined(_WIN32) || defined(WIN32)
  24. LPVOID handle = nullptr;
  25. LPVOID rewind_handle = nullptr;
  26. #else
  27. alignas(64) std::array<u8, default_stack_size> stack;
  28. alignas(64) std::array<u8, default_stack_size> rewind_stack;
  29. u8* stack_limit;
  30. u8* rewind_stack_limit;
  31. boost::context::detail::fcontext_t context;
  32. boost::context::detail::fcontext_t rewind_context;
  33. #endif
  34. };
  35. void Fiber::SetStartParameter(void* new_parameter) {
  36. impl->start_parameter = new_parameter;
  37. }
  38. void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
  39. impl->rewind_point = std::move(rewind_func);
  40. impl->rewind_parameter = rewind_param;
  41. }
  42. #if defined(_WIN32) || defined(WIN32)
  43. void Fiber::Start() {
  44. ASSERT(impl->previous_fiber != nullptr);
  45. impl->previous_fiber->impl->guard.unlock();
  46. impl->previous_fiber.reset();
  47. impl->entry_point(impl->start_parameter);
  48. UNREACHABLE();
  49. }
  50. void Fiber::OnRewind() {
  51. ASSERT(impl->handle != nullptr);
  52. DeleteFiber(impl->handle);
  53. impl->handle = impl->rewind_handle;
  54. impl->rewind_handle = nullptr;
  55. impl->rewind_point(impl->rewind_parameter);
  56. UNREACHABLE();
  57. }
  58. void Fiber::FiberStartFunc(void* fiber_parameter) {
  59. auto* fiber = static_cast<Fiber*>(fiber_parameter);
  60. fiber->Start();
  61. }
  62. void Fiber::RewindStartFunc(void* fiber_parameter) {
  63. auto* fiber = static_cast<Fiber*>(fiber_parameter);
  64. fiber->OnRewind();
  65. }
  66. Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
  67. : impl{std::make_unique<FiberImpl>()} {
  68. impl->entry_point = std::move(entry_point_func);
  69. impl->start_parameter = start_parameter;
  70. impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
  71. }
  72. Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
  73. Fiber::~Fiber() {
  74. if (impl->released) {
  75. return;
  76. }
  77. // Make sure the Fiber is not being used
  78. const bool locked = impl->guard.try_lock();
  79. ASSERT_MSG(locked, "Destroying a fiber that's still running");
  80. if (locked) {
  81. impl->guard.unlock();
  82. }
  83. DeleteFiber(impl->handle);
  84. }
  85. void Fiber::Exit() {
  86. ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
  87. if (!impl->is_thread_fiber) {
  88. return;
  89. }
  90. ConvertFiberToThread();
  91. impl->guard.unlock();
  92. impl->released = true;
  93. }
  94. void Fiber::Rewind() {
  95. ASSERT(impl->rewind_point);
  96. ASSERT(impl->rewind_handle == nullptr);
  97. impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
  98. SwitchToFiber(impl->rewind_handle);
  99. }
  100. void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
  101. ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
  102. ASSERT_MSG(to != nullptr, "Next fiber is null!");
  103. to->impl->guard.lock();
  104. to->impl->previous_fiber = from;
  105. SwitchToFiber(to->impl->handle);
  106. ASSERT(from->impl->previous_fiber != nullptr);
  107. from->impl->previous_fiber->impl->guard.unlock();
  108. from->impl->previous_fiber.reset();
  109. }
  110. std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
  111. std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
  112. fiber->impl->guard.lock();
  113. fiber->impl->handle = ConvertThreadToFiber(nullptr);
  114. fiber->impl->is_thread_fiber = true;
  115. return fiber;
  116. }
  117. #else
  118. void Fiber::Start(boost::context::detail::transfer_t& transfer) {
  119. ASSERT(impl->previous_fiber != nullptr);
  120. impl->previous_fiber->impl->context = transfer.fctx;
  121. impl->previous_fiber->impl->guard.unlock();
  122. impl->previous_fiber.reset();
  123. impl->entry_point(impl->start_parameter);
  124. UNREACHABLE();
  125. }
  126. void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
  127. ASSERT(impl->context != nullptr);
  128. impl->context = impl->rewind_context;
  129. impl->rewind_context = nullptr;
  130. u8* tmp = impl->stack_limit;
  131. impl->stack_limit = impl->rewind_stack_limit;
  132. impl->rewind_stack_limit = tmp;
  133. impl->rewind_point(impl->rewind_parameter);
  134. UNREACHABLE();
  135. }
  136. void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
  137. auto* fiber = static_cast<Fiber*>(transfer.data);
  138. fiber->Start(transfer);
  139. }
  140. void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
  141. auto* fiber = static_cast<Fiber*>(transfer.data);
  142. fiber->OnRewind(transfer);
  143. }
  144. Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
  145. : impl{std::make_unique<FiberImpl>()} {
  146. impl->entry_point = std::move(entry_point_func);
  147. impl->start_parameter = start_parameter;
  148. impl->stack_limit = impl->stack.data();
  149. impl->rewind_stack_limit = impl->rewind_stack.data();
  150. u8* stack_base = impl->stack_limit + default_stack_size;
  151. impl->context =
  152. boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
  153. }
  154. Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
  155. Fiber::~Fiber() {
  156. if (impl->released) {
  157. return;
  158. }
  159. // Make sure the Fiber is not being used
  160. const bool locked = impl->guard.try_lock();
  161. ASSERT_MSG(locked, "Destroying a fiber that's still running");
  162. if (locked) {
  163. impl->guard.unlock();
  164. }
  165. }
  166. void Fiber::Exit() {
  167. ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
  168. if (!impl->is_thread_fiber) {
  169. return;
  170. }
  171. impl->guard.unlock();
  172. impl->released = true;
  173. }
  174. void Fiber::Rewind() {
  175. ASSERT(impl->rewind_point);
  176. ASSERT(impl->rewind_context == nullptr);
  177. u8* stack_base = impl->rewind_stack_limit + default_stack_size;
  178. impl->rewind_context =
  179. boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
  180. boost::context::detail::jump_fcontext(impl->rewind_context, this);
  181. }
  182. void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
  183. ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
  184. ASSERT_MSG(to != nullptr, "Next fiber is null!");
  185. to->impl->guard.lock();
  186. to->impl->previous_fiber = from;
  187. auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
  188. ASSERT(from->impl->previous_fiber != nullptr);
  189. from->impl->previous_fiber->impl->context = transfer.fctx;
  190. from->impl->previous_fiber->impl->guard.unlock();
  191. from->impl->previous_fiber.reset();
  192. }
  193. std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
  194. std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
  195. fiber->impl->guard.lock();
  196. fiber->impl->is_thread_fiber = true;
  197. return fiber;
  198. }
  199. #endif
  200. } // namespace Common