mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-17 08:11:51 +00:00
cellGame: Fix PPU deadlocks on concurrent execution
This commit is contained in:
parent
ef8afa7873
commit
f40602cc59
@ -28,6 +28,10 @@ vm::gvar<CellHddGameStatSet> g_stat_set;
|
||||
vm::gvar<CellHddGameSystemFileParam> g_file_param;
|
||||
vm::gvar<CellHddGameCBResult> g_cb_result;
|
||||
|
||||
stx::init_lock acquire_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
stx::access_lock acquire_access_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
stx::reset_lock acquire_reset_lock(stx::init_mutex& mtx, ppu_thread* ppu = nullptr);
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellGameError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@ -705,7 +709,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
|
||||
lv2_sleep(500);
|
||||
|
||||
const auto init = perm.init.init();
|
||||
const auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@ -789,7 +793,7 @@ error_code cellGamePatchCheck(vm::ptr<CellGameContentSize> size, vm::ptr<void> r
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.init();
|
||||
const auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@ -836,7 +840,7 @@ error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameC
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
auto init = perm.init.init();
|
||||
auto init = acquire_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@ -904,7 +908,7 @@ error_code cellGameContentPermit(ppu_thread& ppu, vm::ptr<char[CELL_GAME_PATH_MA
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.reset();
|
||||
const auto init = acquire_reset_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@ -1204,7 +1208,7 @@ error_code cellGameCreateGameData(vm::ptr<CellGameSetInitParams> init, vm::ptr<c
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto _init = perm.init.access();
|
||||
const auto _init = acquire_access_lock(perm.init);
|
||||
|
||||
lv2_sleep(2000);
|
||||
|
||||
@ -1319,7 +1323,7 @@ error_code cellGameDeleteGameData(vm::cptr<char> dirName)
|
||||
if (!_init)
|
||||
{
|
||||
// Or access it
|
||||
if (auto access = perm.init.access(); access)
|
||||
if (auto access = acquire_access_lock(perm.init); access)
|
||||
{
|
||||
// Cannot remove it when it is accessed by cellGameDataCheck
|
||||
// If it is HG data then resort to remove_gd for ERROR_BROKEN
|
||||
@ -1356,7 +1360,7 @@ error_code cellGameGetParamInt(s32 id, vm::ptr<s32> value)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
@ -1484,7 +1488,7 @@ error_code cellGameGetParamString(s32 id, vm::ptr<char> buf, u32 bufsize)
|
||||
|
||||
lv2_sleep(2000);
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init || perm.mode == content_permission::check_mode::not_set)
|
||||
{
|
||||
@ -1530,7 +1534,7 @@ error_code cellGameSetParamString(s32 id, vm::cptr<char> buf)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init || perm.mode == content_permission::check_mode::not_set)
|
||||
{
|
||||
@ -1569,7 +1573,7 @@ error_code cellGameGetSizeKB(ppu_thread& ppu, vm::ptr<s32> size)
|
||||
|
||||
auto& perm = g_fxo->get<content_permission>();
|
||||
|
||||
const auto init = perm.init.access();
|
||||
const auto init = acquire_access_lock(perm.init);
|
||||
|
||||
if (!init)
|
||||
{
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include <deque>
|
||||
#include "util/tsc.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "util/init_mutex.hpp"
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
#ifdef _MSC_VER
|
||||
@ -1108,6 +1109,61 @@ void fmt_class_string<CellError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
stx::init_lock acquire_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
return mtx.init([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
|
||||
{
|
||||
if (!invoke_count)
|
||||
{
|
||||
// Sleep before waiting on lock
|
||||
lv2_obj::sleep(*ppu);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wake up after acquistion or failure to acquire
|
||||
ppu->check_state();
|
||||
}
|
||||
}, ppu);
|
||||
}
|
||||
|
||||
stx::access_lock acquire_access_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
// TODO: Check if needs to wait
|
||||
return mtx.access();
|
||||
}
|
||||
|
||||
stx::reset_lock acquire_reset_lock(stx::init_mutex& mtx, ppu_thread* ppu)
|
||||
{
|
||||
if (!ppu)
|
||||
{
|
||||
ppu = ensure(cpu_thread::get_current<ppu_thread>());
|
||||
}
|
||||
|
||||
return mtx.reset([](int invoke_count, const stx::init_lock&, ppu_thread* ppu)
|
||||
{
|
||||
if (!invoke_count)
|
||||
{
|
||||
// Sleep before waiting on lock
|
||||
lv2_obj::sleep(*ppu);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wake up after acquistion or failure to acquire
|
||||
ppu->check_state();
|
||||
}
|
||||
}, ppu);
|
||||
}
|
||||
|
||||
class ppu_syscall_usage
|
||||
{
|
||||
// Internal buffer
|
||||
|
@ -20,11 +20,19 @@ namespace stx
|
||||
{
|
||||
init_mutex* _this;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
void invoke_callback(int invoke_count, Func&& func, Args&&... args) const
|
||||
{
|
||||
std::invoke(func, invoke_count, *this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... FAndArgs>
|
||||
explicit init_lock(init_mutex& mtx, FAndArgs&&... args) noexcept
|
||||
template <typename Forced, typename... FAndArgs>
|
||||
explicit init_lock(init_mutex& mtx, Forced&&, FAndArgs&&... args) noexcept
|
||||
: _this(&mtx)
|
||||
{
|
||||
bool invoked_func = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto [val, ok] = _this->m_state.fetch_op([](u32& value)
|
||||
@ -35,7 +43,7 @@ namespace stx
|
||||
return true;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(FAndArgs))
|
||||
if constexpr (Forced()())
|
||||
{
|
||||
if (value & c_init_bit)
|
||||
{
|
||||
@ -50,12 +58,21 @@ namespace stx
|
||||
if (val == 0)
|
||||
{
|
||||
// Success: obtained "init lock"
|
||||
|
||||
if constexpr (sizeof...(FAndArgs))
|
||||
{
|
||||
if (invoked_func)
|
||||
{
|
||||
invoke_callback(1, std::forward<FAndArgs>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (val & c_init_bit)
|
||||
{
|
||||
if constexpr (sizeof...(FAndArgs))
|
||||
if constexpr (Forced()())
|
||||
{
|
||||
// Forced reset
|
||||
val -= c_init_bit - 1;
|
||||
@ -67,22 +84,43 @@ namespace stx
|
||||
val = _this->m_state;
|
||||
}
|
||||
|
||||
// Call specified reset function
|
||||
std::invoke(std::forward<FAndArgs>(args)...);
|
||||
break;
|
||||
}
|
||||
|
||||
// Failure
|
||||
_this = nullptr;
|
||||
|
||||
if constexpr (sizeof...(FAndArgs))
|
||||
{
|
||||
if (invoked_func)
|
||||
{
|
||||
invoke_callback(1, std::forward<FAndArgs>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(FAndArgs))
|
||||
{
|
||||
if (!invoked_func)
|
||||
{
|
||||
invoke_callback(0, std::forward<FAndArgs>(args)...);
|
||||
invoked_func = true;
|
||||
}
|
||||
}
|
||||
|
||||
_this->m_state.wait(val);
|
||||
}
|
||||
}
|
||||
|
||||
init_lock(const init_lock&) = delete;
|
||||
|
||||
init_lock(init_lock&& lock) noexcept
|
||||
: _this(std::exchange(lock._this, nullptr))
|
||||
{
|
||||
}
|
||||
|
||||
init_lock& operator=(const init_lock&) = delete;
|
||||
|
||||
~init_lock()
|
||||
@ -102,6 +140,11 @@ namespace stx
|
||||
|
||||
explicit operator bool() && = delete;
|
||||
|
||||
void force_lock(init_mutex* mtx)
|
||||
{
|
||||
_this = mtx;
|
||||
}
|
||||
|
||||
void cancel() & noexcept
|
||||
{
|
||||
if (_this)
|
||||
@ -112,54 +155,43 @@ namespace stx
|
||||
_this = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
init_mutex* get_mutex()
|
||||
{
|
||||
return _this;
|
||||
}
|
||||
};
|
||||
|
||||
// Obtain exclusive lock to initialize protected resource. Waits for ongoing initialization or finalization. Fails if already initialized.
|
||||
[[nodiscard]] init_lock init() noexcept
|
||||
template <typename... FAndArgs>
|
||||
[[nodiscard]] init_lock init(FAndArgs&&... args) noexcept
|
||||
{
|
||||
return init_lock(*this);
|
||||
return init_lock(*this, std::false_type{}, std::forward<FAndArgs>(args)...);
|
||||
}
|
||||
|
||||
// Same as init, but never fails, and executes provided `on_reset` function if already initialized.
|
||||
template <typename F, typename... Args>
|
||||
[[nodiscard]] init_lock init_always(F on_reset, Args&&... args) noexcept
|
||||
{
|
||||
return init_lock(*this, std::move(on_reset), std::forward<Args>(args)...);
|
||||
init_lock lock(*this, std::true_type{});
|
||||
|
||||
if (!lock)
|
||||
{
|
||||
lock.force_lock(this);
|
||||
std::invoke(std::forward<F>(on_reset), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
class reset_lock final
|
||||
{
|
||||
init_mutex* _this;
|
||||
init_lock _lock;
|
||||
|
||||
public:
|
||||
explicit reset_lock(init_mutex& mtx) noexcept
|
||||
: _this(&mtx)
|
||||
explicit reset_lock(init_lock&& lock) noexcept
|
||||
: _lock(std::move(lock))
|
||||
{
|
||||
auto [val, ok] = _this->m_state.fetch_op([](u32& value)
|
||||
{
|
||||
if (value & c_init_bit)
|
||||
{
|
||||
// Remove initialized state and add "init lock"
|
||||
value -= c_init_bit - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
// Failure: not initialized
|
||||
_this = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
while (val != 1)
|
||||
{
|
||||
// Wait for other users to finish their work
|
||||
_this->m_state.wait(val);
|
||||
val = _this->m_state;
|
||||
}
|
||||
}
|
||||
|
||||
reset_lock(const reset_lock&) = delete;
|
||||
@ -168,36 +200,63 @@ namespace stx
|
||||
|
||||
~reset_lock()
|
||||
{
|
||||
if (_this)
|
||||
if (_lock)
|
||||
{
|
||||
// Set uninitialized state and remove "init lock"
|
||||
_this->m_state -= 1;
|
||||
_this->m_state.notify_all();
|
||||
_lock.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
explicit operator bool() const& noexcept
|
||||
{
|
||||
return _this != nullptr;
|
||||
return !!_lock;
|
||||
}
|
||||
|
||||
explicit operator bool() && = delete;
|
||||
|
||||
void set_init() & noexcept
|
||||
{
|
||||
if (_this)
|
||||
if (_lock)
|
||||
{
|
||||
// Set initialized state (TODO?)
|
||||
_this->m_state |= c_init_bit;
|
||||
_this->m_state.notify_all();
|
||||
_lock.get_mutex()->m_state |= c_init_bit;
|
||||
_lock.get_mutex()->m_state.notify_all();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Obtain exclusive lock to finalize protected resource. Waits for ongoing use. Fails if not initialized.
|
||||
template <typename F, typename... Args>
|
||||
[[nodiscard]] reset_lock reset(F on_reset, Args&&... args) noexcept
|
||||
{
|
||||
init_lock lock(*this, std::true_type{}, std::forward<F>(on_reset), std::forward<Args>(args)...);
|
||||
|
||||
if (!lock)
|
||||
{
|
||||
lock.force_lock(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.cancel();
|
||||
}
|
||||
|
||||
return reset_lock(std::move(lock));
|
||||
}
|
||||
|
||||
[[nodiscard]] reset_lock reset() noexcept
|
||||
{
|
||||
return reset_lock(*this);
|
||||
init_lock lock(*this, std::true_type{});
|
||||
|
||||
if (!lock)
|
||||
{
|
||||
lock.force_lock(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.cancel();
|
||||
}
|
||||
|
||||
return reset_lock(std::move(lock));
|
||||
}
|
||||
|
||||
class access_lock final
|
||||
@ -276,4 +335,8 @@ namespace stx
|
||||
m_state.wait(state);
|
||||
}
|
||||
};
|
||||
|
||||
using init_lock = init_mutex::init_lock;
|
||||
using reset_lock = init_mutex::reset_lock;
|
||||
using access_lock = init_mutex::access_lock;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user