Some things improved

shared_mutex_t implemented
GUI Emu Callbacks rewritten
fxm::import, fxm::import_always implemented
cellMsgDialog rewritten
Emu.CallAfter improved (returns std::future)
This commit is contained in:
Nekotekina 2015-09-18 01:41:14 +03:00
parent 9d68c16c62
commit 8ae3401ffa
77 changed files with 1814 additions and 1831 deletions

View file

@ -1,355 +0,0 @@
#pragma once
template<typename T, size_t size = sizeof(T)> struct _to_atomic_subtype
{
static_assert(size == 1 || size == 2 || size == 4 || size == 8 || size == 16, "Invalid atomic type");
};
template<typename T> struct _to_atomic_subtype<T, 1>
{
using type = u8;
};
template<typename T> struct _to_atomic_subtype<T, 2>
{
using type = u16;
};
template<typename T> struct _to_atomic_subtype<T, 4>
{
using type = u32;
};
template<typename T> struct _to_atomic_subtype<T, 8>
{
using type = u64;
};
template<typename T> struct _to_atomic_subtype<T, 16>
{
using type = u128;
};
template<typename T> using atomic_subtype_t = typename _to_atomic_subtype<T>::type;
// result wrapper to deal with void result type
template<typename T, typename RT, typename VT> struct atomic_op_result_t
{
RT result;
template<typename... Args> inline atomic_op_result_t(T func, VT& var, Args&&... args)
: result(std::move(func(var, std::forward<Args>(args)...)))
{
}
inline RT move()
{
return std::move(result);
}
};
// void specialization: result is the initial value of the first arg
template<typename T, typename VT> struct atomic_op_result_t<T, void, VT>
{
VT result;
template<typename... Args> inline atomic_op_result_t(T func, VT& var, Args&&... args)
: result(var)
{
func(var, std::forward<Args>(args)...);
}
inline VT move()
{
return std::move(result);
}
};
// member function specialization
template<typename CT, typename... FArgs, typename RT, typename VT> struct atomic_op_result_t<RT(CT::*)(FArgs...), RT, VT>
{
RT result;
template<typename... Args> inline atomic_op_result_t(RT(CT::*func)(FArgs...), VT& var, Args&&... args)
: result(std::move((var.*func)(std::forward<Args>(args)...)))
{
}
inline RT move()
{
return std::move(result);
}
};
// member function void specialization
template<typename CT, typename... FArgs, typename VT> struct atomic_op_result_t<void(CT::*)(FArgs...), void, VT>
{
VT result;
template<typename... Args> inline atomic_op_result_t(void(CT::*func)(FArgs...), VT& var, Args&&... args)
: result(var)
{
(var.*func)(std::forward<Args>(args)...);
}
inline VT move()
{
return std::move(result);
}
};
template<typename T> union _atomic_base
{
using type = std::remove_cv_t<T>;
using subtype = atomic_subtype_t<type>;
type data; // unsafe direct access
subtype sub_data; // unsafe direct access to substitute type
force_inline static const subtype to_subtype(const type& value)
{
return reinterpret_cast<const subtype&>(value);
}
force_inline static const type from_subtype(const subtype value)
{
return reinterpret_cast<const type&>(value);
}
force_inline static type& to_type(subtype& value)
{
return reinterpret_cast<type&>(value);
}
private:
template<typename T2> force_inline static void write_relaxed(volatile T2& data, const T2& value)
{
data = value;
}
force_inline static void write_relaxed(volatile u128& data, const u128& value)
{
sync_lock_test_and_set(&data, value);
}
template<typename T2> force_inline static T2 read_relaxed(const volatile T2& data)
{
return data;
}
force_inline static u128 read_relaxed(const volatile u128& value)
{
return sync_val_compare_and_swap(const_cast<volatile u128*>(&value), u128{0}, u128{0});
}
public:
// atomically compare data with cmp, replace with exch if equal, return previous data value anyway
force_inline const type compare_and_swap(const type& cmp, const type& exch) volatile
{
return from_subtype(sync_val_compare_and_swap(&sub_data, to_subtype(cmp), to_subtype(exch)));
}
// atomically compare data with cmp, replace with exch if equal, return true if data was replaced
force_inline bool compare_and_swap_test(const type& cmp, const type& exch) volatile
{
return sync_bool_compare_and_swap(&sub_data, to_subtype(cmp), to_subtype(exch));
}
// read data with memory barrier
force_inline const type load_sync() const volatile
{
const subtype zero = {};
return from_subtype(sync_val_compare_and_swap(const_cast<subtype*>(&sub_data), zero, zero));
}
// atomically replace data with exch, return previous data value
force_inline const type exchange(const type& exch) volatile
{
return from_subtype(sync_lock_test_and_set(&sub_data, to_subtype(exch)));
}
// read data without memory barrier (works as load_sync() for 128 bit)
force_inline const type load() const volatile
{
return from_subtype(read_relaxed(sub_data));
}
// write data without memory barrier (works as exchange() for 128 bit, discarding result)
force_inline void store(const type& value) volatile
{
write_relaxed(sub_data, to_subtype(value));
}
// perform an atomic operation on data (func is either pointer to member function or callable object with a T& first arg);
// returns the result of the callable object call or previous (old) value of the atomic variable if the return type is void
template<typename F, typename... Args, typename RT = std::result_of_t<F(T&, Args...)>> auto atomic_op(F func, Args&&... args) volatile -> decltype(atomic_op_result_t<F, RT, T>::result)
{
while (true)
{
// read the old value from memory
const subtype old = read_relaxed(sub_data);
// copy the old value
subtype _new = old;
// call atomic op for the local copy of the old value and save the return value of the function
atomic_op_result_t<F, RT, T> result(func, to_type(_new), args...);
// 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();
}
}
// atomic bitwise OR, returns previous data
force_inline const type _or(const type& right) volatile
{
return from_subtype(sync_fetch_and_or(&sub_data, to_subtype(right)));
}
// atomic bitwise AND, returns previous data
force_inline const type _and(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&sub_data, to_subtype(right)));
}
// atomic bitwise AND NOT (inverts right argument), returns previous data
force_inline const type _and_not(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&sub_data, ~to_subtype(right)));
}
// atomic bitwise XOR, returns previous data
force_inline const type _xor(const type& right) volatile
{
return from_subtype(sync_fetch_and_xor(&sub_data, to_subtype(right)));
}
force_inline const type operator |=(const type& right) volatile
{
return from_subtype(sync_fetch_and_or(&sub_data, to_subtype(right)) | to_subtype(right));
}
force_inline const type operator &=(const type& right) volatile
{
return from_subtype(sync_fetch_and_and(&sub_data, to_subtype(right)) & to_subtype(right));
}
force_inline const type operator ^=(const type& right) volatile
{
return from_subtype(sync_fetch_and_xor(&sub_data, to_subtype(right)) ^ to_subtype(right));
}
};
template<typename T, typename = if_integral_t<T>> inline T operator ++(_atomic_base<T>& left)
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, 1) + 1);
}
template<typename T, typename = if_integral_t<T>> inline T operator --(_atomic_base<T>& left)
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, 1) - 1);
}
template<typename T, typename = if_integral_t<T>> inline T operator ++(_atomic_base<T>& left, int)
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, 1));
}
template<typename T, typename = if_integral_t<T>> inline T operator --(_atomic_base<T>& left, int)
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, 1));
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator +=(_atomic_base<T>& left, T2 right) -> decltype(std::declval<T>() + std::declval<T2>())
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, right) + right);
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator -=(_atomic_base<T>& left, T2 right) -> decltype(std::declval<T>() - std::declval<T2>())
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, right) - right);
}
template<typename T, typename = if_integral_t<T>> inline le_t<T> operator ++(_atomic_base<le_t<T>>& left)
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, 1) + 1);
}
template<typename T, typename = if_integral_t<T>> inline le_t<T> operator --(_atomic_base<le_t<T>>& left)
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, 1) - 1);
}
template<typename T, typename = if_integral_t<T>> inline le_t<T> operator ++(_atomic_base<le_t<T>>& left, int)
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, 1));
}
template<typename T, typename = if_integral_t<T>> inline le_t<T> operator --(_atomic_base<le_t<T>>& left, int)
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, 1));
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator +=(_atomic_base<le_t<T>>& left, T2 right) -> decltype(std::declval<T>() + std::declval<T2>())
{
return left.from_subtype(sync_fetch_and_add(&left.sub_data, right) + right);
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator -=(_atomic_base<le_t<T>>& left, T2 right) -> decltype(std::declval<T>() - std::declval<T2>())
{
return left.from_subtype(sync_fetch_and_sub(&left.sub_data, right) - right);
}
template<typename T, typename = if_integral_t<T>> inline be_t<T> operator ++(_atomic_base<be_t<T>>& left)
{
return left.atomic_op([](be_t<T>& value) -> be_t<T>
{
return ++value;
});
}
template<typename T, typename = if_integral_t<T>> inline be_t<T> operator --(_atomic_base<be_t<T>>& left)
{
return left.atomic_op([](be_t<T>& value) -> be_t<T>
{
return --value;
});
}
template<typename T, typename = if_integral_t<T>> inline be_t<T> operator ++(_atomic_base<be_t<T>>& left, int)
{
return left.atomic_op([](be_t<T>& value) -> be_t<T>
{
return value++;
});
}
template<typename T, typename = if_integral_t<T>> inline be_t<T> operator --(_atomic_base<be_t<T>>& left, int)
{
return left.atomic_op([](be_t<T>& value) -> be_t<T>
{
return value--;
});
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator +=(_atomic_base<be_t<T>>& left, T2 right) -> be_t<decltype(std::declval<T>() + std::declval<T2>())>
{
return left.atomic_op([right](be_t<T>& value) -> be_t<T>
{
return value += right;
});
}
template<typename T, typename T2, typename = if_integral_t<T>> inline auto operator -=(_atomic_base<be_t<T>>& left, T2 right) -> be_t<decltype(std::declval<T>() - std::declval<T2>())>
{
return left.atomic_op([right](be_t<T>& value) -> be_t<T>
{
return value -= right;
});
}
template<typename T> using atomic_t = _atomic_base<T>; // Atomic Type with native endianness (for emulator memory)
template<typename T> using atomic_be_t = _atomic_base<to_be_t<T>>; // Atomic BE Type (for PS3 virtual memory)
template<typename T> using atomic_le_t = _atomic_base<to_le_t<T>>; // Atomic LE Type (for PSV virtual memory)

View file

@ -77,7 +77,7 @@ namespace vm
void* const g_base_addr = (atexit(finalize), initialize());
void* g_priv_addr;
std::array<atomic_t<u8>, 0x100000000ull / 4096> g_pages = {}; // information about every page
std::array<atomic_t<u8>, 0x100000000ull / 4096> g_pages{}; // information about every page
const thread_ctrl_t* const INVALID_THREAD = reinterpret_cast<const thread_ctrl_t*>(~0ull);
@ -85,16 +85,11 @@ namespace vm
class reservation_mutex_t
{
atomic_t<const thread_ctrl_t*> m_owner;
atomic_t<const thread_ctrl_t*> m_owner{ INVALID_THREAD };
std::condition_variable m_cv;
std::mutex m_mutex;
public:
reservation_mutex_t()
{
m_owner.store(INVALID_THREAD);
}
bool do_notify = false;
never_inline void lock()
@ -105,7 +100,7 @@ namespace vm
while (!m_owner.compare_and_swap_test(INVALID_THREAD, owner))
{
if (m_owner.load() == owner)
if (m_owner == owner)
{
throw EXCEPTION("Deadlock");
}
@ -423,7 +418,7 @@ namespace vm
throw EXCEPTION("Invalid arguments (addr=0x%x, size=0x%x)", addr, size);
}
const u8 flags = g_pages[addr >> 12].load();
const u8 flags = g_pages[addr >> 12];
if (!(flags & page_writable) || !(flags & page_allocated) || (flags & page_no_reservations))
{
@ -587,7 +582,7 @@ namespace vm
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
{
if (g_pages[i].load())
if (g_pages[i])
{
throw EXCEPTION("Memory already mapped (addr=0x%x, size=0x%x, flags=0x%x, current_addr=0x%x)", addr, size, flags, i * 4096);
}
@ -630,7 +625,7 @@ namespace vm
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
{
if ((g_pages[i].load() & flags_test) != (flags_test | page_allocated))
if ((g_pages[i] & flags_test) != (flags_test | page_allocated))
{
return false;
}
@ -677,7 +672,7 @@ namespace vm
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
{
if (!(g_pages[i].load() & page_allocated))
if ((g_pages[i] & page_allocated) == 0)
{
throw EXCEPTION("Memory not mapped (addr=0x%x, size=0x%x, current_addr=0x%x)", addr, size, i * 4096);
}
@ -719,7 +714,7 @@ namespace vm
for (u32 i = addr / 4096; i <= (addr + size - 1) / 4096; i++)
{
if ((g_pages[i].load() & page_allocated) != page_allocated)
if ((g_pages[i] & page_allocated) == 0)
{
return false;
}
@ -788,7 +783,7 @@ namespace vm
// check if memory area is already mapped
for (u32 i = addr / 4096; i <= (addr + size - 1) / 4096; i++)
{
if (g_pages[i].load())
if (g_pages[i])
{
return false;
}
@ -862,7 +857,7 @@ namespace vm
return addr;
}
if (used.load() + size > this->size)
if (used + size > this->size)
{
return 0;
}
@ -941,7 +936,7 @@ namespace vm
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
{
if (g_pages[i].load())
if (g_pages[i])
{
throw EXCEPTION("Unexpected pages allocated (current_addr=0x%x)", i * 4096);
}

View file

@ -269,19 +269,11 @@ namespace vm
}
};
template<typename T> struct cast_ptr<be_t<T>>
template<typename T, bool Se> struct cast_ptr<se_t<T, Se>>
{
force_inline static u32 cast(const be_t<T>& addr, const char* file, int line, const char* func)
force_inline static u32 cast(const se_t<T, Se>& addr, const char* file, int line, const char* func)
{
return cast_ptr<T>::cast(addr.value(), file, line, func);
}
};
template<typename T> struct cast_ptr<le_t<T>>
{
force_inline static u32 cast(const le_t<T>& addr, const char* file, int line, const char* func)
{
return cast_ptr<T>::cast(addr.value(), file, line, func);
return cast_ptr<T>::cast(addr, file, line, func);
}
};