summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt2
-rw-r--r--src/common/fiber.cpp147
-rw-r--r--src/common/fiber.h55
3 files changed, 204 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index c8bf80372..554d6e253 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -110,6 +110,8 @@ add_library(common STATIC
110 common_types.h 110 common_types.h
111 dynamic_library.cpp 111 dynamic_library.cpp
112 dynamic_library.h 112 dynamic_library.h
113 fiber.cpp
114 fiber.h
113 file_util.cpp 115 file_util.cpp
114 file_util.h 116 file_util.h
115 hash.h 117 hash.h
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
new file mode 100644
index 000000000..eb59f1aa9
--- /dev/null
+++ b/src/common/fiber.cpp
@@ -0,0 +1,147 @@
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/fiber.h"
6
7namespace Common {
8
9#ifdef _MSC_VER
10#include <windows.h>
11
12struct Fiber::FiberImpl {
13 LPVOID handle = nullptr;
14};
15
16void Fiber::_start([[maybe_unused]] void* parameter) {
17 guard.lock();
18 if (previous_fiber) {
19 previous_fiber->guard.unlock();
20 previous_fiber = nullptr;
21 }
22 entry_point(start_parameter);
23}
24
25static void __stdcall FiberStartFunc(LPVOID lpFiberParameter)
26{
27 auto fiber = static_cast<Fiber *>(lpFiberParameter);
28 fiber->_start(nullptr);
29}
30
31Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
32 : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} {
33 impl = std::make_unique<FiberImpl>();
34 impl->handle = CreateFiber(0, &FiberStartFunc, this);
35}
36
37Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} {
38 impl = std::make_unique<FiberImpl>();
39}
40
41Fiber::~Fiber() {
42 // Make sure the Fiber is not being used
43 guard.lock();
44 guard.unlock();
45 DeleteFiber(impl->handle);
46}
47
48void Fiber::Exit() {
49 if (!is_thread_fiber) {
50 return;
51 }
52 ConvertFiberToThread();
53 guard.unlock();
54}
55
56void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
57 to->guard.lock();
58 to->previous_fiber = from;
59 SwitchToFiber(to->impl->handle);
60 auto previous_fiber = from->previous_fiber;
61 if (previous_fiber) {
62 previous_fiber->guard.unlock();
63 previous_fiber.reset();
64 }
65}
66
67std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
68 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
69 fiber->guard.lock();
70 fiber->impl->handle = ConvertThreadToFiber(NULL);
71 fiber->is_thread_fiber = true;
72 return fiber;
73}
74
75#else
76
77#include <boost/context/detail/fcontext.hpp>
78
79constexpr std::size_t default_stack_size = 1024 * 1024 * 4; // 4MB
80
81struct Fiber::FiberImpl {
82 boost::context::detail::fcontext_t context;
83 std::array<u8, default_stack_size> stack;
84};
85
86void Fiber::_start(void* parameter) {
87 guard.lock();
88 boost::context::detail::transfer_t* transfer = static_cast<boost::context::detail::transfer_t*>(parameter);
89 if (previous_fiber) {
90 previous_fiber->impl->context = transfer->fctx;
91 previous_fiber->guard.unlock();
92 previous_fiber = nullptr;
93 }
94 entry_point(start_parameter);
95}
96
97static void FiberStartFunc(boost::context::detail::transfer_t transfer)
98{
99 auto fiber = static_cast<Fiber *>(transfer.data);
100 fiber->_start(&transfer);
101}
102
103Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
104 : guard{}, entry_point{std::move(entry_point_func)}, start_parameter{start_parameter}, previous_fiber{} {
105 impl = std::make_unique<FiberImpl>();
106 auto start_func = std::bind(&Fiber::start, this);
107 impl->context =
108 boost::context::detail::make_fcontext(impl->stack.data(), impl->stack.size(), &start_func);
109}
110
111Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} {
112 impl = std::make_unique<FiberImpl>();
113}
114
115Fiber::~Fiber() {
116 // Make sure the Fiber is not being used
117 guard.lock();
118 guard.unlock();
119}
120
121void Fiber::Exit() {
122 if (!is_thread_fiber) {
123 return;
124 }
125 guard.unlock();
126}
127
128void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
129 to->guard.lock();
130 to->previous_fiber = from;
131 auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr);
132 auto previous_fiber = from->previous_fiber;
133 if (previous_fiber) {
134 previous_fiber->impl->context = transfer.fctx;
135 previous_fiber->guard.unlock();
136 previous_fiber.reset();
137 }
138}
139
140std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
141 std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
142 fiber->is_thread_fiber = true;
143 return fiber;
144}
145
146#endif
147} // namespace Common
diff --git a/src/common/fiber.h b/src/common/fiber.h
new file mode 100644
index 000000000..ab44905cf
--- /dev/null
+++ b/src/common/fiber.h
@@ -0,0 +1,55 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <memory>
9
10#include "common/common_types.h"
11#include "common/spin_lock.h"
12
13namespace Common {
14
15class Fiber {
16public:
17 Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter);
18 ~Fiber();
19
20 Fiber(const Fiber&) = delete;
21 Fiber& operator=(const Fiber&) = delete;
22
23 Fiber(Fiber&&) = default;
24 Fiber& operator=(Fiber&&) = default;
25
26 /// Yields control from Fiber 'from' to Fiber 'to'
27 /// Fiber 'from' must be the currently running fiber.
28 static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
29 static std::shared_ptr<Fiber> ThreadToFiber();
30
31 /// Only call from main thread's fiber
32 void Exit();
33
34 /// Used internally but required to be public, Shall not be used
35 void _start(void* parameter);
36
37 /// Changes the start parameter of the fiber. Has no effect if the fiber already started
38 void SetStartParameter(void* new_parameter) {
39 start_parameter = new_parameter;
40 }
41
42private:
43 Fiber();
44
45 struct FiberImpl;
46
47 SpinLock guard;
48 std::function<void(void*)> entry_point;
49 void* start_parameter;
50 std::shared_ptr<Fiber> previous_fiber;
51 std::unique_ptr<FiberImpl> impl;
52 bool is_thread_fiber{};
53};
54
55} // namespace Common