summaryrefslogtreecommitdiff
path: root/src/common/fiber.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/fiber.cpp')
-rw-r--r--src/common/fiber.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
new file mode 100644
index 000000000..3e3029cd1
--- /dev/null
+++ b/src/common/fiber.cpp
@@ -0,0 +1,233 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/fiber.h"
7#include "common/spin_lock.h"
8
9#if defined(_WIN32) || defined(WIN32)
10#include <windows.h>
11#else
12#include <boost/context/detail/fcontext.hpp>
13#endif
14
15namespace Common {
16
17constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
18
19struct Fiber::FiberImpl {
20 SpinLock guard{};
21 std::function<void(void*)> entry_point;
22 std::function<void(void*)> rewind_point;
23 void* rewind_parameter{};
24 void* start_parameter{};
25 std::shared_ptr<Fiber> previous_fiber;
26 bool is_thread_fiber{};
27 bool released{};
28
29#if defined(_WIN32) || defined(WIN32)
30 LPVOID handle = nullptr;
31 LPVOID rewind_handle = nullptr;
32#else
33 alignas(64) std::array<u8, default_stack_size> stack;
34 alignas(64) std::array<u8, default_stack_size> rewind_stack;
35 u8* stack_limit;
36 u8* rewind_stack_limit;
37 boost::context::detail::fcontext_t context;
38 boost::context::detail::fcontext_t rewind_context;
39#endif
40};
41
42void Fiber::SetStartParameter(void* new_parameter) {
43 impl->start_parameter = new_parameter;
44}
45
46void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
47 impl->rewind_point = std::move(rewind_func);
48 impl->rewind_parameter = rewind_param;
49}
50
51#if defined(_WIN32) || defined(WIN32)
52
53void Fiber::Start() {
54 ASSERT(impl->previous_fiber != nullptr);
55 impl->previous_fiber->impl->guard.unlock();
56 impl->previous_fiber.reset();
57 impl->entry_point(impl->start_parameter);
58 UNREACHABLE();
59}
60
61void Fiber::OnRewind() {
62 ASSERT(impl->handle != nullptr);
63 DeleteFiber(impl->handle);
64 impl->handle = impl->rewind_handle;
65 impl->rewind_handle = nullptr;
66 impl->rewind_point(impl->rewind_parameter);
67 UNREACHABLE();
68}
69
70void Fiber::FiberStartFunc(void* fiber_parameter) {
71 auto* fiber = static_cast<Fiber*>(fiber_parameter);
72 fiber->Start();
73}
74
75void Fiber::RewindStartFunc(void* fiber_parameter) {
76 auto* fiber = static_cast<Fiber*>(fiber_parameter);
77 fiber->OnRewind();
78}
79
80Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
81 : impl{std::make_unique<FiberImpl>()} {
82 impl->entry_point = std::move(entry_point_func);
83 impl->start_parameter = start_parameter;
84 impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
85}
86
87Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
88
89Fiber::~Fiber() {
90 if (impl->released) {
91 return;
92 }
93 // Make sure the Fiber is not being used
94 const bool locked = impl->guard.try_lock();
95 ASSERT_MSG(locked, "Destroying a fiber that's still running");
96 if (locked) {
97 impl->guard.unlock();
98 }
99 DeleteFiber(impl->handle);
100}
101
102void Fiber::Exit() {
103 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
104 if (!impl->is_thread_fiber) {
105 return;
106 }
107 ConvertFiberToThread();
108 impl->guard.unlock();
109 impl->released = true;
110}
111
112void Fiber::Rewind() {
113 ASSERT(impl->rewind_point);
114 ASSERT(impl->rewind_handle == nullptr);
115 impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
116 SwitchToFiber(impl->rewind_handle);
117}
118
119void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
120 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
121 ASSERT_MSG(to != nullptr, "Next fiber is null!");
122 to->impl->guard.lock();
123 to->impl->previous_fiber = from;
124 SwitchToFiber(to->impl->handle);
125 ASSERT(from->impl->previous_fiber != nullptr);
126 from->impl->previous_fiber->impl->guard.unlock();
127 from->impl->previous_fiber.reset();
128}
129
130std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
131 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
132 fiber->impl->guard.lock();
133 fiber->impl->handle = ConvertThreadToFiber(nullptr);
134 fiber->impl->is_thread_fiber = true;
135 return fiber;
136}
137
138#else
139
140void Fiber::Start(boost::context::detail::transfer_t& transfer) {
141 ASSERT(impl->previous_fiber != nullptr);
142 impl->previous_fiber->impl->context = transfer.fctx;
143 impl->previous_fiber->impl->guard.unlock();
144 impl->previous_fiber.reset();
145 impl->entry_point(impl->start_parameter);
146 UNREACHABLE();
147}
148
149void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transfer) {
150 ASSERT(impl->context != nullptr);
151 impl->context = impl->rewind_context;
152 impl->rewind_context = nullptr;
153 u8* tmp = impl->stack_limit;
154 impl->stack_limit = impl->rewind_stack_limit;
155 impl->rewind_stack_limit = tmp;
156 impl->rewind_point(impl->rewind_parameter);
157 UNREACHABLE();
158}
159
160void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
161 auto* fiber = static_cast<Fiber*>(transfer.data);
162 fiber->Start(transfer);
163}
164
165void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
166 auto* fiber = static_cast<Fiber*>(transfer.data);
167 fiber->OnRewind(transfer);
168}
169
170Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
171 : impl{std::make_unique<FiberImpl>()} {
172 impl->entry_point = std::move(entry_point_func);
173 impl->start_parameter = start_parameter;
174 impl->stack_limit = impl->stack.data();
175 impl->rewind_stack_limit = impl->rewind_stack.data();
176 u8* stack_base = impl->stack_limit + default_stack_size;
177 impl->context =
178 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
179}
180
181Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
182
183Fiber::~Fiber() {
184 if (impl->released) {
185 return;
186 }
187 // Make sure the Fiber is not being used
188 const bool locked = impl->guard.try_lock();
189 ASSERT_MSG(locked, "Destroying a fiber that's still running");
190 if (locked) {
191 impl->guard.unlock();
192 }
193}
194
195void Fiber::Exit() {
196 ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
197 if (!impl->is_thread_fiber) {
198 return;
199 }
200 impl->guard.unlock();
201 impl->released = true;
202}
203
204void Fiber::Rewind() {
205 ASSERT(impl->rewind_point);
206 ASSERT(impl->rewind_context == nullptr);
207 u8* stack_base = impl->rewind_stack_limit + default_stack_size;
208 impl->rewind_context =
209 boost::context::detail::make_fcontext(stack_base, impl->stack.size(), RewindStartFunc);
210 boost::context::detail::jump_fcontext(impl->rewind_context, this);
211}
212
213void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
214 ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
215 ASSERT_MSG(to != nullptr, "Next fiber is null!");
216 to->impl->guard.lock();
217 to->impl->previous_fiber = from;
218 auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
219 ASSERT(from->impl->previous_fiber != nullptr);
220 from->impl->previous_fiber->impl->context = transfer.fctx;
221 from->impl->previous_fiber->impl->guard.unlock();
222 from->impl->previous_fiber.reset();
223}
224
225std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
226 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
227 fiber->impl->guard.lock();
228 fiber->impl->is_thread_fiber = true;
229 return fiber;
230}
231
232#endif
233} // namespace Common