Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:11:55

0001 #ifndef FWCore_Concurrency_ThreadSafeAddOnlyContainer_h
0002 #define FWCore_Concurrency_ThreadSafeAddOnlyContainer_h
0003 
0004 #include <atomic>
0005 #include <utility>
0006 
0007 // This container will make objects and hold them. The container deletes
0008 // the objects when the container is destroyed and only then. The client
0009 // of the container should not delete them. The client must save the pointers
0010 // to the objects that are returned by the makeAndHold function. There
0011 // is no other way to access them. The pointers remain valid for as long
0012 // as the container still exists.
0013 
0014 // It is safe for multiple threads to concurrently call makeAndHold.
0015 
0016 // Warning, none of the memory used by this is deallocated before the
0017 // entire container is destroyed. If used in the wrong way, this container
0018 // could cause memory hoarding.
0019 
0020 // The original use case for this was that we had complex large objects
0021 // in thread local storage and this was causing problems. Instead we
0022 // we stored the complex objects in this container and used one thread
0023 // local pointer to save the pointer to the object corresponding to
0024 // to each thread. Instead of storing a complex object in thread local
0025 // storage we were able to only store a simple pointer. There may be
0026 // other uses for this.
0027 
0028 namespace edm {
0029 
0030   template <typename T>
0031   class ThreadSafeAddOnlyContainer {
0032   public:
0033     ThreadSafeAddOnlyContainer();
0034 
0035     ~ThreadSafeAddOnlyContainer();
0036 
0037     template <typename... Args>
0038     T* makeAndHold(Args&&... args);
0039 
0040   private:
0041     class Node {
0042     public:
0043       template <typename... Args>
0044       Node(Node* iNext, Args&&... args);
0045       Node const* next() const { return next_; }
0046       void setNext(Node* v) { next_ = v; }
0047       T* address() { return &data_; }
0048 
0049     private:
0050       Node* next_;
0051       T data_;
0052     };
0053 
0054     std::atomic<Node*> front_;
0055   };
0056 
0057   template <typename T>
0058   ThreadSafeAddOnlyContainer<T>::ThreadSafeAddOnlyContainer() : front_(nullptr) {}
0059 
0060   template <typename T>
0061   ThreadSafeAddOnlyContainer<T>::~ThreadSafeAddOnlyContainer() {
0062     Node const* node = front_.load();
0063     while (node) {
0064       Node const* next = node->next();
0065       delete node;
0066       node = next;
0067     }
0068   }
0069 
0070   template <typename T>
0071   template <typename... Args>
0072   T* ThreadSafeAddOnlyContainer<T>::makeAndHold(Args&&... args) {
0073     Node* expected = front_.load();
0074     Node* newNode = new Node(expected, std::forward<Args>(args)...);
0075     while (!front_.compare_exchange_strong(expected, newNode)) {
0076       // another thread changed front_ before us so try again
0077       newNode->setNext(expected);
0078     }
0079     return newNode->address();
0080   }
0081 
0082   template <typename T>
0083   template <typename... Args>
0084   ThreadSafeAddOnlyContainer<T>::Node::Node(Node* iNext, Args&&... args)
0085       : next_(iNext), data_(std::forward<Args>(args)...) {}
0086 }  // namespace edm
0087 
0088 #endif