diff options
author | Fernando Sahmkow <fsahmkow27@gmail.com> | 2020-02-05 20:48:20 +0100 |
---|---|---|
committer | Fernando Sahmkow <fsahmkow27@gmail.com> | 2020-06-18 22:29:15 +0200 |
commit | be320a9e10fda32a984b12cdfe3aaf09cc67b39a (patch) | |
tree | f2bb28be6adc2a416b59393bb8f438636e9c79c1 /src/common | |
parent | Tests: Add tests for fibers and refactor/fix Fiber class (diff) | |
download | yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar.gz yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar.bz2 yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar.lz yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar.xz yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.tar.zst yuzu-be320a9e10fda32a984b12cdfe3aaf09cc67b39a.zip |
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/fiber.cpp | 55 | ||||
-rw-r--r-- | src/common/fiber.h | 14 | ||||
-rw-r--r-- | src/common/spin_lock.cpp | 7 | ||||
-rw-r--r-- | src/common/spin_lock.h | 1 |
4 files changed, 53 insertions, 24 deletions
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp index a2c0401c4..a88a30ced 100644 --- a/src/common/fiber.cpp +++ b/src/common/fiber.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/fiber.h" #ifdef _MSC_VER #include <windows.h> @@ -18,11 +19,11 @@ struct Fiber::FiberImpl { }; void Fiber::start() { - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void __stdcall Fiber::FiberStartFunc(void* fiber_parameter) @@ -43,12 +44,16 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } DeleteFiber(impl->handle); } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -57,14 +62,15 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; SwitchToFiber(to->impl->handle); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr<Fiber> Fiber::ThreadToFiber() { @@ -85,12 +91,12 @@ struct alignas(64) Fiber::FiberImpl { }; void Fiber::start(boost::context::detail::transfer_t& transfer) { - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber = nullptr; - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); entry_point(start_parameter); + UNREACHABLE(); } void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) @@ -113,11 +119,15 @@ Fiber::Fiber() : guard{}, entry_point{}, start_parameter{}, previous_fiber{} { Fiber::~Fiber() { // Make sure the Fiber is not being used - guard.lock(); - guard.unlock(); + bool locked = guard.try_lock(); + ASSERT_MSG(locked, "Destroying a fiber that's still running"); + if (locked) { + guard.unlock(); + } } void Fiber::Exit() { + ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber"); if (!is_thread_fiber) { return; } @@ -125,15 +135,16 @@ void Fiber::Exit() { } void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) { + ASSERT_MSG(from != nullptr, "Yielding fiber is null!"); + ASSERT_MSG(to != nullptr, "Next fiber is null!"); to->guard.lock(); to->previous_fiber = from; auto transfer = boost::context::detail::jump_fcontext(to->impl.context, nullptr); auto previous_fiber = from->previous_fiber; - if (previous_fiber) { - previous_fiber->impl->context = transfer.fctx; - previous_fiber->guard.unlock(); - previous_fiber.reset(); - } + ASSERT(previous_fiber != nullptr); + previous_fiber->impl->context = transfer.fctx; + previous_fiber->guard.unlock(); + previous_fiber.reset(); } std::shared_ptr<Fiber> Fiber::ThreadToFiber() { diff --git a/src/common/fiber.h b/src/common/fiber.h index 812d6644a..89a01fdd8 100644 --- a/src/common/fiber.h +++ b/src/common/fiber.h @@ -18,6 +18,18 @@ namespace boost::context::detail { namespace Common { +/** + * Fiber class + * a fiber is a userspace thread with it's own context. They can be used to + * implement coroutines, emulated threading systems and certain asynchronous + * patterns. + * + * This class implements fibers at a low level, thus allowing greater freedom + * to implement such patterns. This fiber class is 'threadsafe' only one fiber + * can be running at a time and threads will be locked while trying to yield to + * a running fiber until it yields. WARNING exchanging two running fibers between + * threads will cause a deadlock. + */ class Fiber { public: Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter); @@ -53,8 +65,6 @@ private: static void FiberStartFunc(boost::context::detail::transfer_t transfer); #endif - - struct FiberImpl; SpinLock guard; diff --git a/src/common/spin_lock.cpp b/src/common/spin_lock.cpp index 8077b78d2..82a1d39ff 100644 --- a/src/common/spin_lock.cpp +++ b/src/common/spin_lock.cpp @@ -43,4 +43,11 @@ void SpinLock::unlock() { lck.clear(std::memory_order_release); } +bool SpinLock::try_lock() { + if (lck.test_and_set(std::memory_order_acquire)) { + return false; + } + return true; +} + } // namespace Common diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h index cbc67b6c8..70282a961 100644 --- a/src/common/spin_lock.h +++ b/src/common/spin_lock.h @@ -12,6 +12,7 @@ class SpinLock { public: void lock(); void unlock(); + bool try_lock(); private: std::atomic_flag lck = ATOMIC_FLAG_INIT; |