mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-08-08 09:09:46 +00:00
atomic.h, sys_cond/sys_lwcond improved
This commit is contained in:
parent
ffc11bfda3
commit
31774b7262
6 changed files with 52 additions and 38 deletions
|
@ -33,11 +33,11 @@ template<typename T> struct _to_atomic_subtype<T, 16>
|
||||||
template<typename T> using atomic_subtype_t = typename _to_atomic_subtype<T>::type;
|
template<typename T> using atomic_subtype_t = typename _to_atomic_subtype<T>::type;
|
||||||
|
|
||||||
// result wrapper to deal with void result type
|
// result wrapper to deal with void result type
|
||||||
template<typename RT> struct atomic_op_result_t
|
template<typename RT, typename... Args> struct atomic_op_result_t
|
||||||
{
|
{
|
||||||
RT result;
|
RT result;
|
||||||
|
|
||||||
template<typename T, typename... Args> inline atomic_op_result_t(T func, Args&&... args)
|
template<typename T> inline atomic_op_result_t(T func, Args&&... args)
|
||||||
: result(std::move(func(std::forward<Args>(args)...)))
|
: result(std::move(func(std::forward<Args>(args)...)))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -48,16 +48,20 @@ template<typename RT> struct atomic_op_result_t
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// void specialization
|
// void specialization: result is the initial value of the first arg
|
||||||
template<> struct atomic_op_result_t<void>
|
template<typename RT, typename... Args> struct atomic_op_result_t<void, RT&, Args...>
|
||||||
{
|
{
|
||||||
template<typename T, typename... Args> inline atomic_op_result_t(T func, Args&&... args)
|
RT result;
|
||||||
|
|
||||||
|
template<typename T> inline atomic_op_result_t(T func, RT& var, Args&&... args)
|
||||||
|
: result(var)
|
||||||
{
|
{
|
||||||
func(std::forward<Args>(args)...);
|
func(var, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void move()
|
inline RT move()
|
||||||
{
|
{
|
||||||
|
return std::move(result);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,7 +148,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform an atomic operation on data (callable object version, first arg is a reference to atomic type)
|
// perform an atomic operation on data (callable object version, first arg is a reference to atomic type)
|
||||||
template<typename F, typename... Args> auto atomic_op(F func, Args&&... args) volatile -> decltype(func(std::declval<T&>(), args...))
|
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, Args...)>> auto atomic_op(F func, Args&&... args) volatile -> decltype(std::declval<atomic_op_result_t<RT, T&, Args...>>().result)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -155,7 +159,7 @@ public:
|
||||||
subtype _new = old;
|
subtype _new = old;
|
||||||
|
|
||||||
// call atomic op for the local copy of the old value and save the return value of the function
|
// call atomic op for the local copy of the old value and save the return value of the function
|
||||||
atomic_op_result_t<std::result_of_t<F(T&, Args...)>> result(func, to_type(_new), args...);
|
atomic_op_result_t<RT, T&, Args...> result(func, to_type(_new), args...);
|
||||||
|
|
||||||
// atomically compare value with `old`, replace with `_new` and return on success
|
// atomically compare value with `old`, replace with `_new` and return on success
|
||||||
if (sync_bool_compare_and_swap(&sub_data, old, _new)) return result.move();
|
if (sync_bool_compare_and_swap(&sub_data, old, _new)) return result.move();
|
||||||
|
@ -163,7 +167,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform an atomic operation on data (member function version)
|
// perform an atomic operation on data (member function version)
|
||||||
template<typename RT, typename... FArgs, typename CT, typename... Args, typename = std::enable_if_t<std::is_same<T, CT>::value>> auto atomic_op(RT(CT::* func)(FArgs...), Args&&... args) volatile -> decltype((std::declval<T&>().*func)(args...))
|
template<typename RT, typename... FArgs, typename CT, typename... Args> RT atomic_op(RT(CT::* func)(FArgs...), Args&&... args) volatile
|
||||||
|
{
|
||||||
|
return atomic_op(std::mem_fn(func), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... FArgs, typename CT, typename... Args> T atomic_op(void(CT::* func)(FArgs...), Args&&... args) volatile
|
||||||
{
|
{
|
||||||
return atomic_op(std::mem_fn(func), std::forward<Args>(args)...);
|
return atomic_op(std::mem_fn(func), std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,10 @@ SysCallBase sys_cond("sys_cond");
|
||||||
|
|
||||||
extern u64 get_system_time();
|
extern u64 get_system_time();
|
||||||
|
|
||||||
void lv2_cond_t::notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it)
|
void lv2_cond_t::notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread)
|
||||||
{
|
{
|
||||||
CHECK_LV2_LOCK(lv2_lock);
|
CHECK_LV2_LOCK(lv2_lock);
|
||||||
|
|
||||||
auto& thread = *it;
|
|
||||||
|
|
||||||
if (mutex->owner)
|
if (mutex->owner)
|
||||||
{
|
{
|
||||||
// add thread to the mutex sleep queue if cannot lock immediately
|
// add thread to the mutex sleep queue if cannot lock immediately
|
||||||
|
@ -107,7 +105,7 @@ s32 sys_cond_signal(u32 cond_id)
|
||||||
// signal one waiting thread; protocol is ignored in current implementation
|
// signal one waiting thread; protocol is ignored in current implementation
|
||||||
if (!cond->sq.empty())
|
if (!cond->sq.empty())
|
||||||
{
|
{
|
||||||
cond->notify(lv2_lock, cond->sq.begin());
|
cond->notify(lv2_lock, cond->sq.front());
|
||||||
cond->sq.pop_front();
|
cond->sq.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,9 +126,9 @@ s32 sys_cond_signal_all(u32 cond_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// signal all waiting threads; protocol is ignored in current implementation
|
// signal all waiting threads; protocol is ignored in current implementation
|
||||||
for (auto it = cond->sq.begin(); it != cond->sq.end(); it++)
|
for (auto& thread : cond->sq)
|
||||||
{
|
{
|
||||||
cond->notify(lv2_lock, it);
|
cond->notify(lv2_lock, thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
cond->sq.clear();
|
cond->sq.clear();
|
||||||
|
@ -151,22 +149,24 @@ s32 sys_cond_signal_to(u32 cond_id, u32 thread_id)
|
||||||
return CELL_ESRCH;
|
return CELL_ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto found = std::find_if(cond->sq.begin(), cond->sq.end(), [=](sleep_queue_t::value_type& thread)
|
||||||
|
{
|
||||||
|
return thread->get_id() == thread_id;
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: check if CELL_ESRCH is returned if thread_id is invalid
|
// TODO: check if CELL_ESRCH is returned if thread_id is invalid
|
||||||
|
if (found == cond->sq.end())
|
||||||
// signal specified thread (protocol is not required)
|
|
||||||
for (auto it = cond->sq.begin(); it != cond->sq.end(); it++)
|
|
||||||
{
|
{
|
||||||
if ((*it)->get_id() == thread_id)
|
|
||||||
{
|
|
||||||
cond->notify(lv2_lock, it);
|
|
||||||
cond->sq.erase(it);
|
|
||||||
return CELL_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CELL_EPERM;
|
return CELL_EPERM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// signal specified thread
|
||||||
|
cond->notify(lv2_lock, *found);
|
||||||
|
cond->sq.erase(found);
|
||||||
|
|
||||||
|
return CELL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
|
s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
|
||||||
{
|
{
|
||||||
sys_cond.Log("sys_cond_wait(cond_id=0x%x, timeout=%lld)", cond_id, timeout);
|
sys_cond.Log("sys_cond_wait(cond_id=0x%x, timeout=%lld)", cond_id, timeout);
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct lv2_cond_t
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it);
|
void notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread);
|
||||||
};
|
};
|
||||||
|
|
||||||
REG_ID_TYPE(lv2_cond_t, 0x86); // SYS_COND_OBJECT
|
REG_ID_TYPE(lv2_cond_t, 0x86); // SYS_COND_OBJECT
|
||||||
|
|
|
@ -12,11 +12,11 @@ SysCallBase sys_lwcond("sys_lwcond");
|
||||||
|
|
||||||
extern u64 get_system_time();
|
extern u64 get_system_time();
|
||||||
|
|
||||||
void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::iterator it, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2)
|
void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::value_type& thread, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2)
|
||||||
{
|
{
|
||||||
CHECK_LV2_LOCK(lv2_lock);
|
CHECK_LV2_LOCK(lv2_lock);
|
||||||
|
|
||||||
auto& ppu = static_cast<PPUThread&>(*it->get());
|
auto& ppu = static_cast<PPUThread&>(*thread);
|
||||||
|
|
||||||
ppu.GPR[3] = mode2; // set to return CELL_EBUSY
|
ppu.GPR[3] = mode2; // set to return CELL_EBUSY
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ void lv2_lwcond_t::notify(lv2_lock_t & lv2_lock, sleep_queue_t::iterator it, con
|
||||||
{
|
{
|
||||||
if (!mutex->signaled)
|
if (!mutex->signaled)
|
||||||
{
|
{
|
||||||
return mutex->sq.emplace_back(*it);
|
return mutex->sq.emplace_back(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex->signaled--;
|
mutex->signaled--;
|
||||||
|
@ -114,7 +114,7 @@ s32 _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// signal specified waiting thread
|
// signal specified waiting thread
|
||||||
cond->notify(lv2_lock, found, mutex, mode == 2);
|
cond->notify(lv2_lock, *found, mutex, mode == 2);
|
||||||
|
|
||||||
cond->sq.erase(found);
|
cond->sq.erase(found);
|
||||||
|
|
||||||
|
@ -144,9 +144,9 @@ s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode)
|
||||||
// mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased
|
// mode 2: lightweight mutex was not owned by the calling thread and waiter hasn't been increased
|
||||||
|
|
||||||
// signal all waiting threads; protocol is ignored in current implementation
|
// signal all waiting threads; protocol is ignored in current implementation
|
||||||
for (auto it = cond->sq.begin(); it != cond->sq.end(); it++)
|
for (auto& thread : cond->sq)
|
||||||
{
|
{
|
||||||
cond->notify(lv2_lock, it, mutex, mode == 2);
|
cond->notify(lv2_lock, thread, mutex, mode == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// in mode 1, return the amount of threads signaled
|
// in mode 1, return the amount of threads signaled
|
||||||
|
|
|
@ -32,7 +32,7 @@ struct lv2_lwcond_t
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify(lv2_lock_t& lv2_lock, sleep_queue_t::iterator it, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2);
|
void notify(lv2_lock_t& lv2_lock, sleep_queue_t::value_type& thread, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2);
|
||||||
};
|
};
|
||||||
|
|
||||||
REG_ID_TYPE(lv2_lwcond_t, 0x97); // SYS_LWCOND_OBJECT
|
REG_ID_TYPE(lv2_lwcond_t, 0x97); // SYS_LWCOND_OBJECT
|
||||||
|
|
|
@ -200,15 +200,20 @@ public:
|
||||||
force_inline bool IsReady() const { return m_status == Ready; }
|
force_inline bool IsReady() const { return m_status == Ready; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern Emulator Emu;
|
||||||
|
|
||||||
using lv2_lock_t = std::unique_lock<std::mutex>;
|
using lv2_lock_t = std::unique_lock<std::mutex>;
|
||||||
|
|
||||||
|
inline bool check_lv2_lock(lv2_lock_t& lv2_lock)
|
||||||
|
{
|
||||||
|
return lv2_lock.owns_lock() && lv2_lock.mutex() == &Emu.GetCoreMutex();
|
||||||
|
}
|
||||||
|
|
||||||
#define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex())
|
#define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex())
|
||||||
#define LV2_DEFER_LOCK lv2_lock_t lv2_lock
|
#define LV2_DEFER_LOCK lv2_lock_t lv2_lock
|
||||||
#define CHECK_LV2_LOCK(x) if (!(x).owns_lock() || (x).mutex() != &Emu.GetCoreMutex()) throw EXCEPTION("Invalid LV2_LOCK (locked=%d)", (x).owns_lock())
|
#define CHECK_LV2_LOCK(x) if (!check_lv2_lock(x)) throw EXCEPTION("lv2_lock is invalid or not locked")
|
||||||
#define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EXCEPTION("Aborted (emulation stopped)")
|
#define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EXCEPTION("Aborted (emulation stopped)")
|
||||||
|
|
||||||
extern Emulator Emu;
|
|
||||||
|
|
||||||
typedef void(*CallAfterCbType)(std::function<void()> func);
|
typedef void(*CallAfterCbType)(std::function<void()> func);
|
||||||
|
|
||||||
void CallAfter(std::function<void()> func);
|
void CallAfter(std::function<void()> func);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue