xref: /freebsd-src/contrib/llvm-project/llvm/include/llvm/ADT/LazyAtomicPointer.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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