LCOV - code coverage report
Current view: top level - envoy/thread_local - thread_local.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 26 30 86.7 %
Date: 2024-01-05 06:35:25 Functions: 66 205 32.2 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <cstdint>
       4             : #include <functional>
       5             : #include <memory>
       6             : 
       7             : #include "envoy/common/optref.h"
       8             : #include "envoy/common/pure.h"
       9             : #include "envoy/event/dispatcher.h"
      10             : #include "envoy/thread_local/thread_local_object.h"
      11             : 
      12             : #include "source/common/common/assert.h"
      13             : 
      14             : namespace Envoy {
      15             : namespace ThreadLocal {
      16             : 
      17             : /**
      18             :  * An individual allocated TLS slot. When the slot is destroyed the stored thread local will
      19             :  * be freed on each thread.
      20             :  */
      21             : class Slot {
      22             : public:
      23        2320 :   virtual ~Slot() = default;
      24             : 
      25             :   /**
      26             :    * Returns if there is thread local data for this thread.
      27             :    *
      28             :    * This should return true for Envoy worker threads and false for threads which do not have thread
      29             :    * local storage allocated.
      30             :    *
      31             :    * @return true if registerThread has been called for this thread, false otherwise.
      32             :    */
      33             :   virtual bool currentThreadRegistered() PURE;
      34             : 
      35             :   /**
      36             :    * @return ThreadLocalObjectSharedPtr a thread local object stored in the slot.
      37             :    */
      38             :   virtual ThreadLocalObjectSharedPtr get() PURE;
      39             : 
      40             :   /**
      41             :    * This is a helper on top of get() that casts the object stored in the slot to the specified
      42             :    * type. Since the slot only stores pointers to the base interface, the static_cast operates
      43             :    * in production for performance, and the dynamic_cast validates correctness in tests and debug
      44             :    * builds.
      45             :    */
      46       62383 :   template <class T> T& getTyped() {
      47       62383 :     ASSERT(std::dynamic_pointer_cast<T>(get()) != nullptr);
      48       62383 :     return *static_cast<T*>(get().get());
      49       62383 :   }
      50             : 
      51             :   /**
      52             :    * Set thread local data on all threads previously registered via registerThread().
      53             :    * @param initializeCb supplies the functor that will be called *on each thread*. The functor
      54             :    *                     returns the thread local object which is then stored. The storage is via
      55             :    *                     a shared_ptr. Thus, this is a flexible mechanism that can be used to share
      56             :    *                     the same data across all threads or to share different data on each thread.
      57             :    *
      58             :    * NOTE: The initialize callback is not supposed to capture the Slot, or its owner, as the owner
      59             :    * may be destructed in main thread before the update_cb gets called in a worker thread.
      60             :    */
      61             :   using InitializeCb = std::function<ThreadLocalObjectSharedPtr(Event::Dispatcher& dispatcher)>;
      62             :   virtual void set(InitializeCb cb) PURE;
      63             : 
      64             : protected:
      65             :   template <class T> friend class TypedSlot;
      66             : 
      67             :   /**
      68             :    * UpdateCb takes is passed a shared point to the current stored data. Use of
      69             :    * this API is deprecated; please use TypedSlot::runOnAllThreads instead.
      70             :    *
      71             :    * NOTE: The update callback is not supposed to capture the Slot, or its
      72             :    * owner, as the owner may be destructed in main thread before the update_cb
      73             :    * gets called in a worker thread.
      74             :    **/
      75             :   using UpdateCb = std::function<void(ThreadLocalObjectSharedPtr)>;
      76             : 
      77             :   // Callers must use the TypedSlot API, below.
      78             :   virtual void runOnAllThreads(const UpdateCb& update_cb) PURE;
      79             :   virtual void runOnAllThreads(const UpdateCb& update_cb,
      80             :                                const std::function<void()>& complete_cb) PURE;
      81             : 
      82             :   /**
      83             :    * Returns whether or not global threading has been shutdown.
      84             :    *
      85             :    * @return true if global threading has been shutdown or false if not.
      86             :    */
      87             :   virtual bool isShutdown() const PURE;
      88             : };
      89             : 
      90             : using SlotPtr = std::unique_ptr<Slot>;
      91             : 
      92             : /**
      93             :  * Interface used to allocate thread local slots.
      94             :  */
      95             : class SlotAllocator {
      96             : public:
      97        1495 :   virtual ~SlotAllocator() = default;
      98             : 
      99             :   /**
     100             :    * @return SlotPtr a dedicated slot for use in further calls to get(), set(), etc.
     101             :    */
     102             :   virtual SlotPtr allocateSlot() PURE;
     103             : };
     104             : 
     105             : // Provides a typesafe API for slots. The slot data must be derived from
     106             : // ThreadLocalObject. If there is no slot data, you can instantiated TypedSlot
     107             : // with the default type param: TypedSlot<> tls_;
     108             : //
     109             : // TODO(jmarantz): Rename the Slot class to something like RawSlot, where the
     110             : // only reference is from TypedSlot, which we can then rename to Slot.
     111             : template <class T> class TypedSlot {
     112             : public:
     113             :   /**
     114             :    * Helper method to create a unique_ptr for a typed slot. This helper
     115             :    * reduces some verbose parameterization at call-sites.
     116             :    *
     117             :    * @param allocator factory to allocate untyped Slot objects.
     118             :    * @return a TypedSlotPtr<T> (the type is defined below).
     119             :    */
     120         110 :   static std::unique_ptr<TypedSlot> makeUnique(SlotAllocator& allocator) {
     121         110 :     return std::make_unique<TypedSlot>(allocator);
     122         110 :   }
     123             : 
     124         813 :   explicit TypedSlot(SlotAllocator& allocator) : slot_(allocator.allocateSlot()) {}
     125             : 
     126             :   /**
     127             :    * Returns if there is thread local data for this thread.
     128             :    *
     129             :    * This should return true for Envoy worker threads and false for threads which do not have thread
     130             :    * local storage allocated.
     131             :    *
     132             :    * @return true if registerThread has been called for this thread, false otherwise.
     133             :    */
     134           0 :   bool currentThreadRegistered() { return slot_->currentThreadRegistered(); }
     135             : 
     136             :   /**
     137             :    * Set thread local data on all threads previously registered via registerThread().
     138             :    * @param initializeCb supplies the functor that will be called *on each thread*. The functor
     139             :    *                     returns the thread local object which is then stored. The storage is via
     140             :    *                     a shared_ptr. Thus, this is a flexible mechanism that can be used to share
     141             :    *                     the same data across all threads or to share different data on each thread.
     142             :    *
     143             :    * NOTE: The initialize callback is not supposed to capture the Slot, or its owner, as the owner
     144             :    * may be destructed in main thread before the update_cb gets called in a worker thread.
     145             :    */
     146             :   using InitializeCb = std::function<std::shared_ptr<T>(Event::Dispatcher& dispatcher)>;
     147         660 :   void set(InitializeCb cb) { slot_->set(cb); }
     148             : 
     149             :   /**
     150             :    * @return an optional reference to the thread local object.
     151             :    */
     152           0 :   OptRef<T> get() { return getOpt(slot_->get()); }
     153             :   const OptRef<T> get() const { return getOpt(slot_->get()); }
     154             : 
     155             :   /**
     156             :    * Helper function to call methods on T. The caller is responsible
     157             :    * for ensuring that get().has_value() is true.
     158             :    *
     159             :    * @return a pointer to the thread local object.
     160             :    */
     161          81 :   T* operator->() { return &(slot_->getTyped<T>()); }
     162          53 :   const T* operator->() const { return &(slot_->getTyped<T>()); }
     163             : 
     164             :   /**
     165             :    * Helper function to get access to a T&. The caller is responsible for
     166             :    * ensuring that get().has_value() is true.
     167             :    *
     168             :    * @return a reference to the thread local object.
     169             :    */
     170       49016 :   T& operator*() { return slot_->getTyped<T>(); }
     171             :   const T& operator*() const { return slot_->getTyped<T>(); }
     172             : 
     173             :   /**
     174             :    * UpdateCb is passed a mutable pointer to the current stored data. Callers
     175             :    * can assume that the passed-in OptRef has a value if they have called set(),
     176             :    * yielding a non-null shared_ptr, prior to runOnAllThreads().
     177             :    *
     178             :    * NOTE: The update callback is not supposed to capture the TypedSlot, or its
     179             :    * owner, as the owner may be destructed in main thread before the update_cb
     180             :    * gets called in a worker thread.
     181             :    */
     182             :   using UpdateCb = std::function<void(OptRef<T> obj)>;
     183         202 :   void runOnAllThreads(const UpdateCb& cb) { slot_->runOnAllThreads(makeSlotUpdateCb(cb)); }
     184          10 :   void runOnAllThreads(const UpdateCb& cb, const std::function<void()>& complete_cb) {
     185          10 :     slot_->runOnAllThreads(makeSlotUpdateCb(cb), complete_cb);
     186          10 :   }
     187             : 
     188             :   /**
     189             :    * Returns whether or not global threading has been shutdown.
     190             :    *
     191             :    * @return true if global threading has been shutdown or false if not.
     192             :    */
     193           0 :   bool isShutdown() const { return slot_->isShutdown(); };
     194             : 
     195             : private:
     196         409 :   static OptRef<T> getOpt(ThreadLocalObjectSharedPtr obj) {
     197         409 :     if (obj) {
     198         409 :       return OptRef<T>(obj->asType<T>());
     199         409 :     }
     200           0 :     return OptRef<T>();
     201         409 :   }
     202             : 
     203         212 :   Slot::UpdateCb makeSlotUpdateCb(UpdateCb cb) {
     204         409 :     return [cb](ThreadLocalObjectSharedPtr obj) { cb(getOpt(obj)); };
     205         212 :   }
     206             : 
     207             :   const SlotPtr slot_;
     208             : };
     209             : 
     210             : template <class T = ThreadLocalObject> using TypedSlotPtr = std::unique_ptr<TypedSlot<T>>;
     211             : 
     212             : /**
     213             :  * Interface for getting and setting thread local data as well as registering a thread
     214             :  */
     215             : class Instance : public SlotAllocator {
     216             : public:
     217             :   /**
     218             :    * A thread (via its dispatcher) must be registered before set() is called on any allocated slots
     219             :    * to receive thread local data updates.
     220             :    * @param dispatcher supplies the thread's dispatcher.
     221             :    * @param main_thread supplies whether this is the main program thread or not. (The only
     222             :    *                    difference is that callbacks fire immediately on the main thread when posted
     223             :    *                    from the main thread).
     224             :    */
     225             :   virtual void registerThread(Event::Dispatcher& dispatcher, bool main_thread) PURE;
     226             : 
     227             :   /**
     228             :    * This should be called by the main thread before any worker threads start to exit. This will
     229             :    * block TLS removal during slot destruction, given that worker threads are about to call
     230             :    * shutdownThread(). This avoids having to implement de-registration of threads.
     231             :    */
     232             :   virtual void shutdownGlobalThreading() PURE;
     233             : 
     234             :   /**
     235             :    * The owning thread is about to exit. This will free all thread local variables. It must be
     236             :    * called on the thread that is shutting down.
     237             :    */
     238             :   virtual void shutdownThread() PURE;
     239             : 
     240             :   /**
     241             :    * @return Event::Dispatcher& the thread local dispatcher.
     242             :    */
     243             :   virtual Event::Dispatcher& dispatcher() PURE;
     244             : 
     245             :   /**
     246             :    * Returns whether or not global threading has been shutdown.
     247             :    *
     248             :    * @return true if global threading has been shutdown or false if not.
     249             :    */
     250             :   virtual bool isShutdown() const PURE;
     251             : };
     252             : 
     253             : } // namespace ThreadLocal
     254             : } // namespace Envoy

Generated by: LCOV version 1.15