Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-01-09 09:40:44

0001 // Copyright 2017 The Abseil Authors.
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 //      https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014 
0015 #ifndef ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
0016 #define ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_
0017 
0018 #include <atomic>
0019 #include <cassert>
0020 #include <cstdint>
0021 #include <utility>
0022 
0023 #include "absl/base/attributes.h"
0024 #include "absl/base/config.h"
0025 
0026 #if defined(_MSC_VER) && !defined(__clang__)
0027 #define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 0
0028 #else
0029 #define ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT 1
0030 #endif
0031 
0032 #if defined(_MSC_VER)
0033 #define ABSL_HAVE_WORKING_ATOMIC_POINTER 0
0034 #else
0035 #define ABSL_HAVE_WORKING_ATOMIC_POINTER 1
0036 #endif
0037 
0038 namespace absl {
0039 ABSL_NAMESPACE_BEGIN
0040 namespace base_internal {
0041 
0042 template <typename T>
0043 class AtomicHook;
0044 
0045 // To workaround AtomicHook not being constant-initializable on some platforms,
0046 // prefer to annotate instances with `ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES`
0047 // instead of `ABSL_CONST_INIT`.
0048 #if ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
0049 #define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_CONST_INIT
0050 #else
0051 #define ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES
0052 #endif
0053 
0054 // `AtomicHook` is a helper class, templatized on a raw function pointer type,
0055 // for implementing Abseil customization hooks.  It is a callable object that
0056 // dispatches to the registered hook.  Objects of type `AtomicHook` must have
0057 // static or thread storage duration.
0058 //
0059 // A default constructed object performs a no-op (and returns a default
0060 // constructed object) if no hook has been registered.
0061 //
0062 // Hooks can be pre-registered via constant initialization, for example:
0063 //
0064 // ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES static AtomicHook<void(*)()>
0065 //     my_hook(DefaultAction);
0066 //
0067 // and then changed at runtime via a call to `Store()`.
0068 //
0069 // Reads and writes guarantee memory_order_acquire/memory_order_release
0070 // semantics.
0071 template <typename ReturnType, typename... Args>
0072 class AtomicHook<ReturnType (*)(Args...)> {
0073  public:
0074   using FnPtr = ReturnType (*)(Args...);
0075 
0076   // Constructs an object that by default performs a no-op (and
0077   // returns a default constructed object) when no hook as been registered.
0078   constexpr AtomicHook() : AtomicHook(DummyFunction) {}
0079 
0080   // Constructs an object that by default dispatches to/returns the
0081   // pre-registered default_fn when no hook has been registered at runtime.
0082 #if ABSL_HAVE_WORKING_ATOMIC_POINTER && ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
0083   explicit constexpr AtomicHook(FnPtr default_fn)
0084       : hook_(default_fn), default_fn_(default_fn) {}
0085 #elif ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
0086   explicit constexpr AtomicHook(FnPtr default_fn)
0087       : hook_(kUninitialized), default_fn_(default_fn) {}
0088 #else
0089   // As of January 2020, on all known versions of MSVC this constructor runs in
0090   // the global constructor sequence.  If `Store()` is called by a dynamic
0091   // initializer, we want to preserve the value, even if this constructor runs
0092   // after the call to `Store()`.  If not, `hook_` will be
0093   // zero-initialized by the linker and we have no need to set it.
0094   // https://developercommunity.visualstudio.com/content/problem/336946/class-with-constexpr-constructor-not-using-static.html
0095   explicit constexpr AtomicHook(FnPtr default_fn)
0096       : /* hook_(deliberately omitted), */ default_fn_(default_fn) {
0097     static_assert(kUninitialized == 0, "here we rely on zero-initialization");
0098   }
0099 #endif
0100 
0101   // Stores the provided function pointer as the value for this hook.
0102   //
0103   // This is intended to be called once.  Multiple calls are legal only if the
0104   // same function pointer is provided for each call.  The store is implemented
0105   // as a memory_order_release operation, and read accesses are implemented as
0106   // memory_order_acquire.
0107   void Store(FnPtr fn) {
0108     bool success = DoStore(fn);
0109     static_cast<void>(success);
0110     assert(success);
0111   }
0112 
0113   // Invokes the registered callback.  If no callback has yet been registered, a
0114   // default-constructed object of the appropriate type is returned instead.
0115   template <typename... CallArgs>
0116   ReturnType operator()(CallArgs&&... args) const {
0117     return DoLoad()(std::forward<CallArgs>(args)...);
0118   }
0119 
0120   // Returns the registered callback, or nullptr if none has been registered.
0121   // Useful if client code needs to conditionalize behavior based on whether a
0122   // callback was registered.
0123   //
0124   // Note that atomic_hook.Load()() and atomic_hook() have different semantics:
0125   // operator()() will perform a no-op if no callback was registered, while
0126   // Load()() will dereference a null function pointer.  Prefer operator()() to
0127   // Load()() unless you must conditionalize behavior on whether a hook was
0128   // registered.
0129   FnPtr Load() const {
0130     FnPtr ptr = DoLoad();
0131     return (ptr == DummyFunction) ? nullptr : ptr;
0132   }
0133 
0134  private:
0135   static ReturnType DummyFunction(Args...) {
0136     return ReturnType();
0137   }
0138 
0139   // Current versions of MSVC (as of September 2017) have a broken
0140   // implementation of std::atomic<T*>:  Its constructor attempts to do the
0141   // equivalent of a reinterpret_cast in a constexpr context, which is not
0142   // allowed.
0143   //
0144   // This causes an issue when building with LLVM under Windows.  To avoid this,
0145   // we use a less-efficient, intptr_t-based implementation on Windows.
0146 #if ABSL_HAVE_WORKING_ATOMIC_POINTER
0147   // Return the stored value, or DummyFunction if no value has been stored.
0148   FnPtr DoLoad() const { return hook_.load(std::memory_order_acquire); }
0149 
0150   // Store the given value.  Returns false if a different value was already
0151   // stored to this object.
0152   bool DoStore(FnPtr fn) {
0153     assert(fn);
0154     FnPtr expected = default_fn_;
0155     const bool store_succeeded = hook_.compare_exchange_strong(
0156         expected, fn, std::memory_order_acq_rel, std::memory_order_acquire);
0157     const bool same_value_already_stored = (expected == fn);
0158     return store_succeeded || same_value_already_stored;
0159   }
0160 
0161   std::atomic<FnPtr> hook_;
0162 #else  // !ABSL_HAVE_WORKING_ATOMIC_POINTER
0163   // Use a sentinel value unlikely to be the address of an actual function.
0164   static constexpr intptr_t kUninitialized = 0;
0165 
0166   static_assert(sizeof(intptr_t) >= sizeof(FnPtr),
0167                 "intptr_t can't contain a function pointer");
0168 
0169   FnPtr DoLoad() const {
0170     const intptr_t value = hook_.load(std::memory_order_acquire);
0171     if (value == kUninitialized) {
0172       return default_fn_;
0173     }
0174     return reinterpret_cast<FnPtr>(value);
0175   }
0176 
0177   bool DoStore(FnPtr fn) {
0178     assert(fn);
0179     const auto value = reinterpret_cast<intptr_t>(fn);
0180     intptr_t expected = kUninitialized;
0181     const bool store_succeeded = hook_.compare_exchange_strong(
0182         expected, value, std::memory_order_acq_rel, std::memory_order_acquire);
0183     const bool same_value_already_stored = (expected == value);
0184     return store_succeeded || same_value_already_stored;
0185   }
0186 
0187   std::atomic<intptr_t> hook_;
0188 #endif
0189 
0190   const FnPtr default_fn_;
0191 };
0192 
0193 #undef ABSL_HAVE_WORKING_ATOMIC_POINTER
0194 #undef ABSL_HAVE_WORKING_CONSTEXPR_STATIC_INIT
0195 
0196 }  // namespace base_internal
0197 ABSL_NAMESPACE_END
0198 }  // namespace absl
0199 
0200 #endif  // ABSL_BASE_INTERNAL_ATOMIC_HOOK_H_