#include #include #include #include constexpr uintptr_t WAIT_FREE_SENTINEL = ~uintptr_t(0); template struct intptr_ops; template <> struct intptr_ops<4> { static uintptr_t load(void *ptr) { return emscripten_atomic_load_u32(ptr); } static uintptr_t compare_and_swap(void *ptr, uintptr_t expected, uintptr_t new_value) { return emscripten_atomic_cas_u32(ptr, expected, new_value); } }; template <> struct intptr_ops<8> { static uintptr_t load(void *ptr) { return emscripten_atomic_load_u64(ptr); } static uintptr_t compare_and_swap(void *ptr, uintptr_t expected, uintptr_t new_value) { return emscripten_atomic_cas_u64(ptr, expected, new_value); } }; template class WaitFreeGuard; template class WaitFreeWrap { private: T *value; T *swapUnconditional(T *new_value) { while (true) { uintptr_t current = intptr_ops<>::load(&value); if (current == intptr_ops<>::compare_and_swap( &value, current, reinterpret_cast(new_value))) { return reinterpret_cast(current); } } } friend class WaitFreeGuard; public: WaitFreeWrap(T *value) : value(value) {} std::pair swap(T *new_value, uint32_t timeoutMs = 0) { const auto start = std::chrono::steady_clock::now(); while (timeoutMs == 0 || std::chrono::steady_clock::now() - start < std::chrono::milliseconds(timeoutMs)) { uintptr_t current = intptr_ops<>::load(&value); if (current == WAIT_FREE_SENTINEL) { continue; } if (current == intptr_ops<>::compare_and_swap( &value, current, reinterpret_cast(new_value))) { return {reinterpret_cast(current), true}; } } return {nullptr, false}; } }; template class WaitFreeGuard { private: T *heldValue; WaitFreeWrap &wrap; public: WaitFreeGuard(WaitFreeWrap &wrap) : wrap(wrap) { heldValue = wrap.swapUnconditional(reinterpret_cast(WAIT_FREE_SENTINEL)); } ~WaitFreeGuard() { wrap.swapUnconditional(heldValue); } T *operator->() { return heldValue; } T *operator*() { return heldValue; } T *get() { return heldValue; } };