fiber.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. #if defined(_WIN32) || defined(WIN32)
  7. #include <windows.h>
  8. #else
  9. #include <boost/context/detail/fcontext.hpp>
  10. #endif
  11. namespace Common {
  12. #if defined(_WIN32) || defined(WIN32)
  13. struct Fiber::FiberImpl {
  14. LPVOID handle = nullptr;
  15. };
  16. void Fiber::start() {
  17. ASSERT(previous_fiber != nullptr);
  18. previous_fiber->guard.unlock();
  19. previous_fiber.reset();
  20. entry_point(start_parameter);
  21. UNREACHABLE();
  22. }
  23. void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) {
  24. auto fiber = static_cast<Fiber*>(fiber_parameter);
  25. fiber->start();
  26. }
  27. Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
  28. : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter},
  29. previous_fiber{} {
  30. impl = std::make_unique<FiberImpl>();
  31. impl->handle = CreateFiber(0, &FiberStartFunc, this);
  32. }
  33. Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} {
  34. impl = std::make_unique<FiberImpl>();
  35. }
  36. Fiber::~Fiber() {
  37. // Make sure the Fiber is not being used
  38. bool locked = guard.try_lock();
  39. ASSERT_MSG(locked, "Destroying a fiber that's still running");
  40. if (locked) {
  41. guard.unlock();
  42. }
  43. DeleteFiber(impl->handle);
  44. }
  45. void Fiber::Exit() {
  46. ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
  47. if (!is_thread_fiber) {
  48. return;
  49. }
  50. ConvertFiberToThread();
  51. guard.unlock();
  52. }
  53. void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
  54. ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
  55. ASSERT_MSG(to != nullptr, "Next fiber is null!");
  56. to->guard.lock();
  57. to->previous_fiber = from;
  58. SwitchToFiber(to->impl->handle);
  59. auto previous_fiber = from->previous_fiber;
  60. ASSERT(previous_fiber != nullptr);
  61. previous_fiber->guard.unlock();
  62. previous_fiber.reset();
  63. }
  64. std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
  65. std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
  66. fiber->guard.lock();
  67. fiber->impl->handle = ConvertThreadToFiber(NULL);
  68. fiber->is_thread_fiber = true;
  69. return fiber;
  70. }
  71. #else
  72. constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB
  73. struct alignas(64) Fiber::FiberImpl {
  74. std::array<u8, default_stack_size> stack;
  75. boost::context::detail::fcontext_t context;
  76. };
  77. void Fiber::start(boost::context::detail::transfer_t& transfer) {
  78. ASSERT(previous_fiber != nullptr);
  79. previous_fiber->impl->context = transfer.fctx;
  80. previous_fiber->guard.unlock();
  81. previous_fiber.reset();
  82. entry_point(start_parameter);
  83. UNREACHABLE();
  84. }
  85. void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
  86. auto fiber = static_cast<Fiber*>(transfer.data);
  87. fiber->start(transfer);
  88. }
  89. Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
  90. : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter},
  91. previous_fiber{} {
  92. impl = std::make_unique<FiberImpl>();
  93. impl->context = boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(),
  94. FiberStartFunc);
  95. }
  96. Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} {
  97. impl = std::make_unique<FiberImpl>();
  98. }
  99. Fiber::~Fiber() {
  100. // Make sure the Fiber is not being used
  101. bool locked = guard.try_lock();
  102. ASSERT_MSG(locked, "Destroying a fiber that's still running");
  103. if (locked) {
  104. guard.unlock();
  105. }
  106. }
  107. void Fiber::Exit() {
  108. ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
  109. if (!is_thread_fiber) {
  110. return;
  111. }
  112. guard.unlock();
  113. }
  114. void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
  115. ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
  116. ASSERT_MSG(to != nullptr, "Next fiber is null!");
  117. to->guard.lock();
  118. to->previous_fiber = from;
  119. auto transfer = boost::context::detail::jump_fcontext(to->impl->context, nullptr);
  120. auto previous_fiber = from->previous_fiber;
  121. ASSERT(previous_fiber != nullptr);
  122. previous_fiber->impl->context = transfer.fctx;
  123. previous_fiber->guard.unlock();
  124. previous_fiber.reset();
  125. }
  126. std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
  127. std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
  128. fiber->guard.lock();
  129. fiber->is_thread_fiber = true;
  130. return fiber;
  131. }
  132. #endif
  133. } // namespace Common