atomic_op() rewritten, atomic.h refactoring
cellSync refactoring, wait_op() rewritten, bugfixes
This commit is contained in:
+15
-25
@@ -165,13 +165,6 @@ union u128
|
||||
|
||||
} _bit;
|
||||
|
||||
//operator u64() const { return _u64[0]; }
|
||||
//operator u32() const { return _u32[0]; }
|
||||
//operator u16() const { return _u16[0]; }
|
||||
//operator u8() const { return _u8[0]; }
|
||||
|
||||
//operator bool() const { return _u64[0] != 0 || _u64[1] != 0; }
|
||||
|
||||
static u128 from64(u64 _0, u64 _1 = 0)
|
||||
{
|
||||
u128 ret;
|
||||
@@ -443,7 +436,7 @@ static force_inline u128 sync_fetch_and_xor(volatile u128* dest, u128 value)
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, int size = sizeof(T)> struct se_t;
|
||||
template<typename T, std::size_t Size = sizeof(T)> struct se_t;
|
||||
|
||||
template<typename T> struct se_t<T, 2>
|
||||
{
|
||||
@@ -501,16 +494,13 @@ template<typename T> struct se_t<T, 16>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, T _value, size_t size = sizeof(T)> struct const_se_t;
|
||||
|
||||
template<u8 _value> struct const_se_t<u8, _value, 1>
|
||||
{
|
||||
static const u8 value = _value;
|
||||
};
|
||||
template<typename T, T _value, std::size_t size = sizeof(T)> struct const_se_t;
|
||||
|
||||
template<u16 _value> struct const_se_t<u16, _value, 2>
|
||||
{
|
||||
static const u16 value = ((_value >> 8) & 0xff) | ((_value << 8) & 0xff00);
|
||||
static const u16 value =
|
||||
((_value >> 8) & 0x00ff) |
|
||||
((_value << 8) & 0xff00);
|
||||
};
|
||||
|
||||
template<u32 _value> struct const_se_t<u32, _value, 4>
|
||||
@@ -600,9 +590,9 @@ public:
|
||||
using stype = be_storage_t<std::remove_cv_t<T>>;
|
||||
|
||||
#ifdef IS_LE_MACHINE
|
||||
stype m_data;
|
||||
stype m_data; // don't access directly
|
||||
#else
|
||||
type m_data;
|
||||
type m_data; // don't access directly
|
||||
#endif
|
||||
|
||||
static_assert(!std::is_class<type>::value, "be_t<> error: invalid type (class or structure)");
|
||||
@@ -695,41 +685,41 @@ public:
|
||||
be_t& operator --() { *this -= 1; return *this; }
|
||||
};
|
||||
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value, bool> operator ==(const be_t<T1>& left, const be_t<T2>& right)
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value && std::is_integral<T1>::value, bool> operator ==(const be_t<T1>& left, const be_t<T2>& right)
|
||||
{
|
||||
return left.data() == right.data();
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value, bool> operator !=(const be_t<T1>& left, const be_t<T2>& right)
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value && std::is_integral<T1>::value, bool> operator !=(const be_t<T1>& left, const be_t<T2>& right)
|
||||
{
|
||||
return left.data() != right.data();
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value, be_t<T1>> operator &(const be_t<T1>& left, const be_t<T2>& right)
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value && std::is_integral<T1>::value, be_t<T1>> operator &(const be_t<T1>& left, const be_t<T2>& right)
|
||||
{
|
||||
be_t<T1> result;
|
||||
result.m_data = left.data() & right.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value, be_t<T1>> operator |(const be_t<T1>& left, const be_t<T2>& right)
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value && std::is_integral<T1>::value, be_t<T1>> operator |(const be_t<T1>& left, const be_t<T2>& right)
|
||||
{
|
||||
be_t<T1> result;
|
||||
result.m_data = left.data() | right.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value, be_t<T1>> operator ^(const be_t<T1>& left, const be_t<T2>& right)
|
||||
template<typename T1, typename T2> inline std::enable_if_t<std::is_same<T1, T2>::value && std::is_integral<T1>::value, be_t<T1>> operator ^(const be_t<T1>& left, const be_t<T2>& right)
|
||||
{
|
||||
be_t<T1> result;
|
||||
result.m_data = left.data() ^ right.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T1> inline std::enable_if_t<true, be_t<T1>> operator ~(const be_t<T1>& other)
|
||||
template<typename T1> inline std::enable_if_t<std::is_integral<T1>::value, be_t<T1>> operator ~(const be_t<T1>& arg)
|
||||
{
|
||||
be_t<T1> result;
|
||||
result.m_data = ~other.data();
|
||||
result.m_data = ~arg.data();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -782,7 +772,7 @@ public:
|
||||
using type = std::remove_cv_t<T>;
|
||||
using stype = be_storage_t<std::remove_cv_t<T>>;
|
||||
|
||||
type m_data;
|
||||
type m_data; // don't access directly
|
||||
|
||||
static_assert(!std::is_class<type>::value, "le_t<> error: invalid type (class or structure)");
|
||||
static_assert(!std::is_union<type>::value || std::is_same<type, u128>::value, "le_t<> error: invalid type (union)");
|
||||
|
||||
@@ -749,7 +749,7 @@ size_t get_x64_access_size(x64_context* context, x64_op_t op, x64_reg_t reg, siz
|
||||
if (op == X64OP_CMPXCHG)
|
||||
{
|
||||
// detect whether this instruction can't actually modify memory to avoid breaking reservation;
|
||||
// this may theoretically cause endless loop, but it shouldn't be a problem if only read_sync() generates such instruction
|
||||
// this may theoretically cause endless loop, but it shouldn't be a problem if only load_sync() generates such instruction
|
||||
u64 cmp, exch;
|
||||
if (!get_x64_reg_value(context, reg, d_size, i_size, cmp) || !get_x64_reg_value(context, X64R_RAX, d_size, i_size, exch))
|
||||
{
|
||||
@@ -1480,16 +1480,22 @@ bool thread_t::joinable() const
|
||||
return m_state == TS_JOINABLE;
|
||||
}
|
||||
|
||||
bool waiter_map_t::is_stopped(u64 signal_id)
|
||||
bool waiter_map_t::is_stopped(u32 addr)
|
||||
{
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
LOG_WARNING(Log::HLE, "%s: waiter_op() aborted (signal_id=0x%llx)", name.c_str(), signal_id);
|
||||
LOG_WARNING(Log::HLE, "%s: waiter_op() aborted (addr=0x%x)", name.c_str(), addr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void waiter_map_t::notify(u32 addr)
|
||||
{
|
||||
// signal appropriate condition variable
|
||||
cv[get_hash(addr)].notify_all();
|
||||
}
|
||||
|
||||
const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };
|
||||
const std::function<bool()> SQUEUE_NEVER_EXIT = [](){ return false; };
|
||||
|
||||
|
||||
+29
-26
@@ -103,7 +103,7 @@ class slw_shared_mutex_t
|
||||
|
||||
struct waiter_map_t
|
||||
{
|
||||
static const size_t size = 32;
|
||||
static const size_t size = 16;
|
||||
|
||||
std::array<std::mutex, size> mutex;
|
||||
std::array<std::condition_variable, size> cv;
|
||||
@@ -115,40 +115,43 @@ struct waiter_map_t
|
||||
{
|
||||
}
|
||||
|
||||
bool is_stopped(u64 signal_id);
|
||||
// generate simple "hash" for mutex/cv distribution
|
||||
u32 get_hash(u32 addr)
|
||||
{
|
||||
addr ^= addr >> 16;
|
||||
addr ^= addr >> 24;
|
||||
addr ^= addr >> 28;
|
||||
return addr % size;
|
||||
}
|
||||
|
||||
// check emu status
|
||||
bool is_stopped(u32 addr);
|
||||
|
||||
// wait until waiter_func() returns true, signal_id is an arbitrary number
|
||||
template<typename S, typename WT> force_inline safe_buffers void wait_op(const S& signal_id, const WT waiter_func)
|
||||
template<typename F, typename... Args> safe_buffers auto wait_op(u32 addr, F pred, Args&&... args) -> decltype(static_cast<void>(pred(args...)))
|
||||
{
|
||||
// generate hash
|
||||
const auto hash = std::hash<S>()(signal_id) % size;
|
||||
const u32 hash = get_hash(addr);
|
||||
|
||||
// set mutex locker
|
||||
std::unique_lock<std::mutex> locker(mutex[hash], std::defer_lock);
|
||||
std::unique_lock<std::mutex> lock(mutex[hash], std::defer_lock);
|
||||
|
||||
// check the condition or if the emulator is stopped
|
||||
while (!waiter_func() && !is_stopped(signal_id))
|
||||
while (true)
|
||||
{
|
||||
// check the condition
|
||||
if (pred(args...)) return;
|
||||
|
||||
// lock the mutex and initialize waiter (only once)
|
||||
if (!locker.owns_lock())
|
||||
{
|
||||
locker.lock();
|
||||
}
|
||||
if (!lock) lock.lock();
|
||||
|
||||
// wait on appropriate condition variable for 1 ms or until signal arrived
|
||||
cv[hash].wait_for(locker, std::chrono::milliseconds(1));
|
||||
cv[hash].wait_for(lock, std::chrono::milliseconds(1));
|
||||
|
||||
if (is_stopped(addr)) return;
|
||||
}
|
||||
}
|
||||
|
||||
// signal all threads waiting on waiter_op() with the same signal_id (signaling only hints those threads that corresponding conditions are *probably* met)
|
||||
template<typename S> force_inline void notify(const S& signal_id)
|
||||
{
|
||||
// generate hash
|
||||
const auto hash = std::hash<S>()(signal_id) % size;
|
||||
|
||||
// signal appropriate condition variable
|
||||
cv[hash].notify_all();
|
||||
}
|
||||
void notify(u32 addr);
|
||||
};
|
||||
|
||||
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
|
||||
@@ -209,7 +212,7 @@ public:
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op_sync(SQSVR_OK, [&pos](squeue_sync_var_t& sync) -> u32
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
@@ -272,7 +275,7 @@ public:
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op_sync(SQSVR_OK, [&pos](squeue_sync_var_t& sync) -> u32
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
@@ -341,7 +344,7 @@ public:
|
||||
assert(start_pos < sq_size);
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op_sync(SQSVR_OK, [&pos, start_pos](squeue_sync_var_t& sync) -> u32
|
||||
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
@@ -425,7 +428,7 @@ public:
|
||||
{
|
||||
u32 pos, count;
|
||||
|
||||
while (m_sync.atomic_op_sync(SQSVR_OK, [&pos, &count](squeue_sync_var_t& sync) -> u32
|
||||
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
@@ -463,7 +466,7 @@ public:
|
||||
|
||||
void clear()
|
||||
{
|
||||
while (m_sync.atomic_op_sync(SQSVR_OK, [](squeue_sync_var_t& sync) -> u32
|
||||
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
Reference in New Issue
Block a user