rpcsx/rpcs3/util/shared_cptr.cpp
Nekotekina 1e7a02badb Implement shared_cptr and atomic_cptr
Limited shared_ptr with atomic support.
Atomic version is only partially implemented.
2020-01-20 16:53:42 +03:00

74 lines
1.3 KiB
C++

#include "shared_cptr.hpp"
#include <thread>
stx::atomic_base::ptr_type stx::atomic_base::ref_halve() const noexcept
{
ptr_type v = val_load();
while (true)
{
if (!(v & c_ref_mask))
{
// Nullptr or depleted reference pool
return 0;
}
else if (val_compare_exchange(v, (v & ~c_ref_mask) | (v & c_ref_mask) >> 1))
{
break;
}
}
// Return acquired references (rounded towards zero)
return (v & ~c_ref_mask) | ((v & c_ref_mask) - ((v & c_ref_mask) >> 1) - 1);
}
stx::atomic_base::ptr_type stx::atomic_base::ref_load() const noexcept
{
ptr_type v = val_load();
while (true)
{
if (!(v & c_ref_mask))
{
if (v == 0)
{
// Null pointer
return 0;
}
// Busy wait
std::this_thread::yield();
v = val_load();
}
else if (val_compare_exchange(v, v - 1))
{
break;
}
}
// Obtained 1 reference from the atomic pointer
return v & ~c_ref_mask;
}
void stx::atomic_base::ref_fix(stx::atomic_base::ptr_type& _old) const noexcept
{
ptr_type old = _old & ~c_ref_mask;
ptr_type v = val_load();
while (true)
{
if ((v & ~c_ref_mask) != old || (v & c_ref_mask) == c_ref_mask)
{
// Can't return a reference to the original atomic pointer, so keep it
_old += 1;
return;
}
if (val_compare_exchange(v, v + 1))
{
break;
}
}
}