fiber.cpp 4.1 KB

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