rpcsx/rpcs3/Emu/Memory/atomic.h

366 lines
9.4 KiB
C
Raw Normal View History

2014-09-27 22:49:33 +04:00
#pragma once
2015-02-17 03:08:23 +03:00
#undef InterlockedExchange
#undef InterlockedCompareExchange
#undef InterlockedOr
#undef InterlockedAnd
#undef InterlockedXor
2014-09-27 22:49:33 +04:00
template<typename T, size_t size = sizeof(T)>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype
2014-09-27 22:49:33 +04:00
{
2015-03-02 05:10:41 +03:00
static_assert(size == 1 || size == 2 || size == 4 || size == 8 || size == 16, "Invalid atomic type");
2014-09-27 22:49:33 +04:00
};
template<typename T>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype<T, 1>
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
using type = uint8_t;
2014-09-27 22:49:33 +04:00
};
template<typename T>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype<T, 2>
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
using type = uint16_t;
2014-09-27 22:49:33 +04:00
};
template<typename T>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype<T, 4>
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
using type = uint32_t;
2014-09-27 22:49:33 +04:00
};
template<typename T>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype<T, 8>
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
using type = uint64_t;
2014-09-27 22:49:33 +04:00
};
2015-03-02 05:10:41 +03:00
template<typename T>
2015-03-13 02:18:38 +03:00
struct _to_atomic_subtype<T, 16>
2015-03-02 05:10:41 +03:00
{
2015-03-13 02:18:38 +03:00
using type = u128;
2015-03-02 05:10:41 +03:00
};
2014-09-27 22:49:33 +04:00
template<typename T>
2015-03-13 02:18:38 +03:00
union _atomic_base
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
using type = typename std::remove_cv<T>::type;
using subtype = typename _to_atomic_subtype<type, sizeof(type)>::type;
type data; // unsafe direct access
2015-03-13 04:59:25 +03:00
subtype sub_data; // unsafe direct access to substitute type
2015-03-13 02:18:38 +03:00
__forceinline static const subtype to_subtype(const type& value)
{
return reinterpret_cast<const subtype&>(value);
}
__forceinline static const type from_subtype(const subtype value)
{
return reinterpret_cast<const type&>(value);
}
__forceinline static type& to_type(subtype& value)
{
return reinterpret_cast<type&>(value);
}
2014-09-27 22:49:33 +04:00
public:
// atomically compare data with cmp, replace with exch if equal, return previous data value anyway
2015-03-13 04:59:25 +03:00
__forceinline const type compare_and_swap(const type& cmp, const type& exch) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedCompareExchange(&sub_data, to_subtype(exch), to_subtype(cmp)));
2014-09-27 22:49:33 +04:00
}
// atomically compare data with cmp, replace with exch if equal, return true if data was replaced
2015-03-13 04:59:25 +03:00
__forceinline bool compare_and_swap_test(const type& cmp, const type& exch) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return InterlockedCompareExchangeTest(&sub_data, to_subtype(exch), to_subtype(cmp));
2014-09-27 22:49:33 +04:00
}
// read data with memory barrier
2015-03-13 04:59:25 +03:00
__forceinline const type read_sync() const volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedCompareExchange(const_cast<subtype*>(&sub_data), 0, 0));
2014-09-27 22:49:33 +04:00
}
// atomically replace data with exch, return previous data value
2015-03-13 04:59:25 +03:00
__forceinline const type exchange(const type& exch) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedExchange(&sub_data, to_subtype(exch)));
2014-09-27 22:49:33 +04:00
}
// read data without memory barrier
2015-03-13 04:59:25 +03:00
__forceinline const type read_relaxed() const volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
const subtype value = const_cast<const subtype&>(sub_data);
return from_subtype(value);
2014-09-27 22:49:33 +04:00
}
// write data without memory barrier
2015-03-13 04:59:25 +03:00
__forceinline void write_relaxed(const type& value) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
const_cast<subtype&>(sub_data) = to_subtype(value);
2014-09-27 22:49:33 +04:00
}
// perform atomic operation on data
2015-03-13 04:59:25 +03:00
template<typename FT> __forceinline void atomic_op(const FT atomic_proc) volatile
2014-09-27 22:49:33 +04:00
{
while (true)
{
2015-03-13 02:18:38 +03:00
const subtype old = const_cast<const subtype&>(sub_data);
subtype _new = old;
atomic_proc(to_type(_new)); // function should accept reference to T type
if (InterlockedCompareExchangeTest(&sub_data, _new, old)) return;
2014-09-27 22:49:33 +04:00
}
}
// perform atomic operation on data with special exit condition (if intermediate result != proceed_value)
2015-03-13 04:59:25 +03:00
template<typename RT, typename FT> __forceinline RT atomic_op(const RT proceed_value, const FT atomic_proc) volatile
2014-09-27 22:49:33 +04:00
{
while (true)
{
2015-03-13 02:18:38 +03:00
const subtype old = const_cast<const subtype&>(sub_data);
subtype _new = old;
auto res = static_cast<RT>(atomic_proc(to_type(_new))); // function should accept reference to T type and return some value
2014-09-27 22:49:33 +04:00
if (res != proceed_value) return res;
2015-03-13 02:18:38 +03:00
if (InterlockedCompareExchangeTest(&sub_data, _new, old)) return proceed_value;
2014-09-27 22:49:33 +04:00
}
}
2014-09-28 23:10:13 +04:00
// perform atomic operation on data with additional memory barrier
2015-03-13 04:59:25 +03:00
template<typename FT> __forceinline void atomic_op_sync(const FT atomic_proc) volatile
2014-09-28 23:10:13 +04:00
{
2015-03-13 02:18:38 +03:00
subtype old = InterlockedCompareExchange(&sub_data, 0, 0);
2014-09-28 23:10:13 +04:00
while (true)
{
2015-03-13 02:18:38 +03:00
subtype _new = old;
atomic_proc(to_type(_new)); // function should accept reference to T type
const subtype val = InterlockedCompareExchange(&sub_data, _new, old);
if (val == old) return;
2014-09-28 23:10:13 +04:00
old = val;
}
}
// perform atomic operation on data with additional memory barrier and special exit condition (if intermediate result != proceed_value)
2015-03-13 04:59:25 +03:00
template<typename RT, typename FT> __forceinline RT atomic_op_sync(const RT proceed_value, const FT atomic_proc) volatile
2014-09-28 23:10:13 +04:00
{
2015-03-13 02:18:38 +03:00
subtype old = InterlockedCompareExchange(&sub_data, 0, 0);
2014-09-28 23:10:13 +04:00
while (true)
{
2015-03-13 02:18:38 +03:00
subtype _new = old;
auto res = static_cast<RT>(atomic_proc(to_type(_new))); // function should accept reference to T type and return some value
2014-09-28 23:10:13 +04:00
if (res != proceed_value) return res;
2015-03-13 02:18:38 +03:00
const subtype val = InterlockedCompareExchange(&sub_data, _new, old);
if (val == old) return proceed_value;
2014-09-28 23:10:13 +04:00
old = val;
}
}
2014-09-27 22:49:33 +04:00
// atomic bitwise OR, returns previous data
2015-03-13 04:59:25 +03:00
__forceinline const type _or(const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedOr(&sub_data, to_subtype(right)));
2014-09-27 22:49:33 +04:00
}
// atomic bitwise AND, returns previous data
2015-03-13 04:59:25 +03:00
__forceinline const type _and(const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedAnd(&sub_data, to_subtype(right)));
2014-09-27 22:49:33 +04:00
}
2014-09-28 23:10:13 +04:00
// atomic bitwise AND NOT (inverts right argument), returns previous data
2015-03-13 04:59:25 +03:00
__forceinline const type _and_not(const type& right) volatile
2014-09-28 23:10:13 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedAnd(&sub_data, ~to_subtype(right)));
2014-09-28 23:10:13 +04:00
}
2014-09-27 22:49:33 +04:00
// atomic bitwise XOR, returns previous data
2015-03-13 04:59:25 +03:00
__forceinline const type _xor(const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedXor(&sub_data, to_subtype(right)));
2014-09-27 22:49:33 +04:00
}
2015-03-13 04:59:25 +03:00
__forceinline const type operator |= (const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedOr(&sub_data, to_subtype(right)) | to_subtype(right));
2014-09-27 22:49:33 +04:00
}
2015-03-13 04:59:25 +03:00
__forceinline const type operator &= (const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedAnd(&sub_data, to_subtype(right)) & to_subtype(right));
2014-09-27 22:49:33 +04:00
}
2015-03-13 04:59:25 +03:00
__forceinline const type operator ^= (const type& right) volatile
2014-09-27 22:49:33 +04:00
{
2015-03-13 02:18:38 +03:00
return from_subtype(InterlockedXor(&sub_data, to_subtype(right)) ^ to_subtype(right));
2014-09-27 22:49:33 +04:00
}
};
2015-03-11 19:58:50 +03:00
// Helper definitions
2015-03-13 18:06:27 +03:00
template<typename T, typename T2 = T> using if_arithmetic_t = const typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<T2>::value, T>::type;
template<typename T, typename T2 = T> using if_arithmetic_be_t = const typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<T2>::value, be_t<T>>::type;
template<typename T, typename T2 = T> using if_arithmetic_atomic_t = typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<T2>::value, _atomic_base<T>&>::type;
template<typename T, typename T2 = T> using if_arithmetic_atomic_be_t = typename std::enable_if<std::is_arithmetic<T>::value && std::is_arithmetic<T2>::value, _atomic_base<be_t<T>>&>::type;
2015-03-11 19:58:50 +03:00
template<typename T> inline static if_arithmetic_t<T> operator ++(_atomic_base<T>& left)
2015-03-09 22:56:55 +03:00
{
2015-03-10 19:27:08 +03:00
T result;
2015-03-09 22:56:55 +03:00
2015-03-11 19:58:50 +03:00
left.atomic_op([&result](T& value)
{
result = ++value;
});
return result;
}
template<typename T> inline static if_arithmetic_t<T> operator --(_atomic_base<T>& left)
{
T result;
left.atomic_op([&result](T& value)
{
result = --value;
});
return result;
}
template<typename T> inline static if_arithmetic_t<T> operator ++(_atomic_base<T>& left, int)
{
T result;
left.atomic_op([&result](T& value)
2015-03-09 22:56:55 +03:00
{
result = value++;
});
return result;
}
2015-03-11 19:58:50 +03:00
template<typename T> inline static if_arithmetic_t<T> operator --(_atomic_base<T>& left, int)
2015-03-09 22:56:55 +03:00
{
2015-03-10 22:16:31 +03:00
T result;
2015-03-09 22:56:55 +03:00
2015-03-11 19:58:50 +03:00
left.atomic_op([&result](T& value)
2015-03-09 22:56:55 +03:00
{
result = value--;
});
return result;
}
2015-03-11 19:58:50 +03:00
template<typename T, typename T2> inline static if_arithmetic_t<T, T2> operator +=(_atomic_base<T>& left, T2 right)
2015-03-09 22:56:55 +03:00
{
2015-03-10 19:27:08 +03:00
T result;
2015-03-09 22:56:55 +03:00
2015-03-11 19:58:50 +03:00
left.atomic_op([&result, right](T& value)
2015-03-09 22:56:55 +03:00
{
result = (value += right);
});
return result;
}
2015-03-11 19:58:50 +03:00
template<typename T, typename T2> inline static if_arithmetic_t<T, T2> operator -=(_atomic_base<T>& left, T2 right)
2015-03-10 19:27:08 +03:00
{
T result;
2015-03-11 19:58:50 +03:00
left.atomic_op([&result, right](T& value)
{
result = (value -= right);
});
return result;
}
template<typename T> inline static if_arithmetic_be_t<T> operator ++(_atomic_base<be_t<T>>& left)
{
be_t<T> result;
left.atomic_op([&result](be_t<T>& value)
{
result = ++value;
});
return result;
}
template<typename T> inline static if_arithmetic_be_t<T> operator --(_atomic_base<be_t<T>>& left)
{
be_t<T> result;
left.atomic_op([&result](be_t<T>& value)
{
result = --value;
});
return result;
}
template<typename T> inline static if_arithmetic_be_t<T> operator ++(_atomic_base<be_t<T>>& left, int)
{
be_t<T> result;
left.atomic_op([&result](be_t<T>& value)
{
result = value++;
});
return result;
}
template<typename T> inline static if_arithmetic_be_t<T> operator --(_atomic_base<be_t<T>>& left, int)
{
be_t<T> result;
left.atomic_op([&result](be_t<T>& value)
{
result = value--;
});
return result;
}
template<typename T, typename T2> inline static if_arithmetic_be_t<T, T2> operator +=(_atomic_base<be_t<T>>& left, T2 right)
{
be_t<T> result;
left.atomic_op([&result, right](be_t<T>& value)
{
result = (value += right);
});
return result;
}
template<typename T, typename T2> inline static if_arithmetic_be_t<T, T2> operator -=(_atomic_base<be_t<T>>& left, T2 right)
{
be_t<T> result;
2015-03-10 19:27:08 +03:00
left.atomic_op([&result, right](be_t<T>& value)
{
result = (value -= right);
});
return result;
}
2015-03-09 04:57:50 +03:00
template<typename T> using atomic_le_t = _atomic_base<T>;
2014-09-27 22:49:33 +04:00
2015-03-09 04:57:50 +03:00
template<typename T> using atomic_be_t = _atomic_base<typename to_be_t<T>::type>;
2014-09-27 22:49:33 +04:00
namespace ps3
{
2015-03-09 04:57:50 +03:00
template<typename T> using atomic_t = atomic_be_t<T>;
2014-09-27 22:49:33 +04:00
}
namespace psv
{
2015-03-09 04:57:50 +03:00
template<typename T> using atomic_t = atomic_le_t<T>;
2014-09-27 22:49:33 +04:00
}
using namespace ps3;