15f757f3fSDimitry Andric //===- LazyAtomicPointer.----------------------------------------*- C++ -*-===// 25f757f3fSDimitry Andric // 35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f757f3fSDimitry Andric // 75f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 85f757f3fSDimitry Andric 95f757f3fSDimitry Andric #ifndef LLVM_ADT_LAZYATOMICPOINTER_H 105f757f3fSDimitry Andric #define LLVM_ADT_LAZYATOMICPOINTER_H 115f757f3fSDimitry Andric 125f757f3fSDimitry Andric #include "llvm/ADT/STLFunctionalExtras.h" 135f757f3fSDimitry Andric #include "llvm/Support/Compiler.h" 145f757f3fSDimitry Andric #include <assert.h> 155f757f3fSDimitry Andric #include <atomic> 165f757f3fSDimitry Andric 175f757f3fSDimitry Andric namespace llvm { 185f757f3fSDimitry Andric 195f757f3fSDimitry Andric /// Atomic pointer that's lock-free, but that can coordinate concurrent writes 205f757f3fSDimitry Andric /// from a lazy generator. Should be reserved for cases where concurrent uses of 215f757f3fSDimitry Andric /// a generator for the same storage is unlikely. 225f757f3fSDimitry Andric /// 235f757f3fSDimitry Andric /// The laziness comes in with \a loadOrGenerate(), which lazily calls the 245f757f3fSDimitry Andric /// provided generator ONLY when the value is currently \c nullptr. With 255f757f3fSDimitry Andric /// concurrent calls, only one generator is called and the rest see that value. 265f757f3fSDimitry Andric /// 275f757f3fSDimitry Andric /// Most other APIs treat an in-flight \a loadOrGenerate() as if \c nullptr 285f757f3fSDimitry Andric /// were stored. APIs that are required to write a value will spin. 295f757f3fSDimitry Andric /// 305f757f3fSDimitry Andric /// The underlying storage is \a std::atomic<uintptr_t>. 315f757f3fSDimitry Andric /// 325f757f3fSDimitry Andric /// TODO: In C++20, use std::atomic<T>::wait() instead of spinning and call 335f757f3fSDimitry Andric /// std::atomic<T>::notify_all() in \a loadOrGenerate(). 345f757f3fSDimitry Andric template <class T> class LazyAtomicPointer { 355f757f3fSDimitry Andric static constexpr uintptr_t getNull() { return 0; } 36*0fca6ea1SDimitry Andric static constexpr uintptr_t getBusy() { return UINTPTR_MAX; } 375f757f3fSDimitry Andric 385f757f3fSDimitry Andric static T *makePointer(uintptr_t Value) { 395f757f3fSDimitry Andric assert(Value != getBusy()); 405f757f3fSDimitry Andric return Value ? reinterpret_cast<T *>(Value) : nullptr; 415f757f3fSDimitry Andric } 425f757f3fSDimitry Andric static uintptr_t makeRaw(T *Value) { 435f757f3fSDimitry Andric uintptr_t Raw = Value ? reinterpret_cast<uintptr_t>(Value) : getNull(); 445f757f3fSDimitry Andric assert(Raw != getBusy()); 455f757f3fSDimitry Andric return Raw; 465f757f3fSDimitry Andric } 475f757f3fSDimitry Andric 485f757f3fSDimitry Andric public: 495f757f3fSDimitry Andric /// Store a value. Waits for concurrent \a loadOrGenerate() calls. 505f757f3fSDimitry Andric void store(T *Value) { return (void)exchange(Value); } 515f757f3fSDimitry Andric 525f757f3fSDimitry Andric /// Set a value. Return the old value. Waits for concurrent \a 535f757f3fSDimitry Andric /// loadOrGenerate() calls. 545f757f3fSDimitry Andric T *exchange(T *Value) { 555f757f3fSDimitry Andric // Note: the call to compare_exchange_weak() fails "spuriously" if the 565f757f3fSDimitry Andric // current value is \a getBusy(), causing the loop to spin. 575f757f3fSDimitry Andric T *Old = nullptr; 585f757f3fSDimitry Andric while (!compare_exchange_weak(Old, Value)) { 595f757f3fSDimitry Andric } 605f757f3fSDimitry Andric return Old; 615f757f3fSDimitry Andric } 625f757f3fSDimitry Andric 635f757f3fSDimitry Andric /// Compare-exchange. Returns \c false if there is a concurrent \a 645f757f3fSDimitry Andric /// loadOrGenerate() call, setting \p ExistingValue to \c nullptr. 655f757f3fSDimitry Andric bool compare_exchange_weak(T *&ExistingValue, T *NewValue) { 665f757f3fSDimitry Andric uintptr_t RawExistingValue = makeRaw(ExistingValue); 675f757f3fSDimitry Andric if (Storage.compare_exchange_weak(RawExistingValue, makeRaw(NewValue))) 685f757f3fSDimitry Andric return true; 695f757f3fSDimitry Andric 705f757f3fSDimitry Andric /// Report the existing value as "None" if busy. 715f757f3fSDimitry Andric if (RawExistingValue == getBusy()) 725f757f3fSDimitry Andric ExistingValue = nullptr; 735f757f3fSDimitry Andric else 745f757f3fSDimitry Andric ExistingValue = makePointer(RawExistingValue); 755f757f3fSDimitry Andric return false; 765f757f3fSDimitry Andric } 775f757f3fSDimitry Andric 785f757f3fSDimitry Andric /// Compare-exchange. Keeps trying if there is a concurrent 795f757f3fSDimitry Andric /// \a loadOrGenerate() call. 805f757f3fSDimitry Andric bool compare_exchange_strong(T *&ExistingValue, T *NewValue) { 815f757f3fSDimitry Andric uintptr_t RawExistingValue = makeRaw(ExistingValue); 825f757f3fSDimitry Andric const uintptr_t OriginalRawExistingValue = RawExistingValue; 835f757f3fSDimitry Andric if (Storage.compare_exchange_strong(RawExistingValue, makeRaw(NewValue))) 845f757f3fSDimitry Andric return true; 855f757f3fSDimitry Andric 865f757f3fSDimitry Andric /// Keep trying as long as it's busy. 875f757f3fSDimitry Andric if (LLVM_UNLIKELY(RawExistingValue == getBusy())) { 885f757f3fSDimitry Andric while (RawExistingValue == getBusy()) { 895f757f3fSDimitry Andric RawExistingValue = OriginalRawExistingValue; 905f757f3fSDimitry Andric if (Storage.compare_exchange_weak(RawExistingValue, makeRaw(NewValue))) 915f757f3fSDimitry Andric return true; 925f757f3fSDimitry Andric } 935f757f3fSDimitry Andric } 945f757f3fSDimitry Andric ExistingValue = makePointer(RawExistingValue); 955f757f3fSDimitry Andric return false; 965f757f3fSDimitry Andric } 975f757f3fSDimitry Andric 985f757f3fSDimitry Andric /// Return the current stored value. Returns \a None if there is a concurrent 995f757f3fSDimitry Andric /// \a loadOrGenerate() in flight. 1005f757f3fSDimitry Andric T *load() const { 1015f757f3fSDimitry Andric uintptr_t RawValue = Storage.load(); 1025f757f3fSDimitry Andric return RawValue == getBusy() ? nullptr : makePointer(RawValue); 1035f757f3fSDimitry Andric } 1045f757f3fSDimitry Andric 1055f757f3fSDimitry Andric /// Get the current value, or call \p Generator to generate a value. 1065f757f3fSDimitry Andric /// Guarantees that only one thread's \p Generator will run. 1075f757f3fSDimitry Andric /// 1085f757f3fSDimitry Andric /// \pre \p Generator doesn't return \c nullptr. 1095f757f3fSDimitry Andric T &loadOrGenerate(function_ref<T *()> Generator) { 1105f757f3fSDimitry Andric // Return existing value, if already set. 1115f757f3fSDimitry Andric uintptr_t Raw = Storage.load(); 1125f757f3fSDimitry Andric if (Raw != getNull() && Raw != getBusy()) 1135f757f3fSDimitry Andric return *makePointer(Raw); 1145f757f3fSDimitry Andric 1155f757f3fSDimitry Andric // Try to mark as busy, then generate and store a new value. 1165f757f3fSDimitry Andric if (LLVM_LIKELY(Raw == getNull() && 1175f757f3fSDimitry Andric Storage.compare_exchange_strong(Raw, getBusy()))) { 1185f757f3fSDimitry Andric Raw = makeRaw(Generator()); 1195f757f3fSDimitry Andric assert(Raw != getNull() && "Expected non-null from generator"); 1205f757f3fSDimitry Andric Storage.store(Raw); 1215f757f3fSDimitry Andric return *makePointer(Raw); 1225f757f3fSDimitry Andric } 1235f757f3fSDimitry Andric 1245f757f3fSDimitry Andric // Contended with another generator. Wait for it to complete. 1255f757f3fSDimitry Andric while (Raw == getBusy()) 1265f757f3fSDimitry Andric Raw = Storage.load(); 1275f757f3fSDimitry Andric assert(Raw != getNull() && "Expected non-null from competing generator"); 1285f757f3fSDimitry Andric return *makePointer(Raw); 1295f757f3fSDimitry Andric } 1305f757f3fSDimitry Andric 1315f757f3fSDimitry Andric explicit operator bool() const { return load(); } 1325f757f3fSDimitry Andric operator T *() const { return load(); } 1335f757f3fSDimitry Andric 1345f757f3fSDimitry Andric T &operator*() const { 1355f757f3fSDimitry Andric T *P = load(); 1365f757f3fSDimitry Andric assert(P && "Unexpected null dereference"); 1375f757f3fSDimitry Andric return *P; 1385f757f3fSDimitry Andric } 1395f757f3fSDimitry Andric T *operator->() const { return &operator*(); } 1405f757f3fSDimitry Andric 1415f757f3fSDimitry Andric LazyAtomicPointer() : Storage(0) {} 1425f757f3fSDimitry Andric LazyAtomicPointer(std::nullptr_t) : Storage(0) {} 1435f757f3fSDimitry Andric LazyAtomicPointer(T *Value) : Storage(makeRaw(Value)) {} 1445f757f3fSDimitry Andric LazyAtomicPointer(const LazyAtomicPointer &RHS) 1455f757f3fSDimitry Andric : Storage(makeRaw(RHS.load())) {} 1465f757f3fSDimitry Andric 1475f757f3fSDimitry Andric LazyAtomicPointer &operator=(std::nullptr_t) { 1485f757f3fSDimitry Andric store(nullptr); 1495f757f3fSDimitry Andric return *this; 1505f757f3fSDimitry Andric } 1515f757f3fSDimitry Andric LazyAtomicPointer &operator=(T *RHS) { 1525f757f3fSDimitry Andric store(RHS); 1535f757f3fSDimitry Andric return *this; 1545f757f3fSDimitry Andric } 1555f757f3fSDimitry Andric LazyAtomicPointer &operator=(const LazyAtomicPointer &RHS) { 1565f757f3fSDimitry Andric store(RHS.load()); 1575f757f3fSDimitry Andric return *this; 1585f757f3fSDimitry Andric } 1595f757f3fSDimitry Andric 1605f757f3fSDimitry Andric private: 1615f757f3fSDimitry Andric std::atomic<uintptr_t> Storage; 1625f757f3fSDimitry Andric }; 1635f757f3fSDimitry Andric 1645f757f3fSDimitry Andric } // end namespace llvm 1655f757f3fSDimitry Andric 1665f757f3fSDimitry Andric #endif // LLVM_ADT_LAZYATOMICPOINTER_H 167