-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14213 from wddgit/threadSafeAddOnlyContainer
Add new template class ThreadSafeAddOnlyContainer
- Loading branch information
Showing
3 changed files
with
149 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#ifndef FWCore_Concurrency_ThreadSafeAddOnlyContainer_h | ||
#define FWCore_Concurrency_ThreadSafeAddOnlyContainer_h | ||
|
||
#include <atomic> | ||
#include <utility> | ||
|
||
// This container will make objects and hold them. The container deletes | ||
// the objects when the container is destroyed and only then. The client | ||
// of the container should not delete them. The client must save the pointers | ||
// to the objects that are returned by the makeAndHold function. There | ||
// is no other way to access them. The pointers remain valid for as long | ||
// as the container still exists. | ||
|
||
// It is safe for multiple threads to concurrently call makeAndHold. | ||
|
||
// Warning, none of the memory used by this is deallocated before the | ||
// entire container is destroyed. If used in the wrong way, this container | ||
// could cause memory hoarding. | ||
|
||
// The original use case for this was that we had complex large objects | ||
// in thread local storage and this was causing problems. Instead we | ||
// we stored the complex objects in this container and used one thread | ||
// local pointer to save the pointer to the object corresponding to | ||
// to each thread. Instead of storing a complex object in thread local | ||
// storage we were able to only store a simple pointer. There may be | ||
// other uses for this. | ||
|
||
namespace edm { | ||
|
||
template <typename T> | ||
class ThreadSafeAddOnlyContainer { | ||
public: | ||
|
||
ThreadSafeAddOnlyContainer(); | ||
|
||
~ThreadSafeAddOnlyContainer(); | ||
|
||
template <typename... Args> | ||
T* makeAndHold(Args&&... args); | ||
|
||
private: | ||
|
||
class Node { | ||
public: | ||
|
||
template <typename... Args> | ||
Node(Node* iNext, Args&&... args); | ||
Node const* next() const { return next_; } | ||
void setNext(Node* v) { next_ = v; } | ||
T* address() { return &data_; } | ||
|
||
private: | ||
|
||
Node* next_; | ||
T data_; | ||
}; | ||
|
||
std::atomic<Node*> front_; | ||
}; | ||
|
||
template <typename T> | ||
ThreadSafeAddOnlyContainer<T>::ThreadSafeAddOnlyContainer() : | ||
front_(nullptr) { | ||
} | ||
|
||
template <typename T> | ||
ThreadSafeAddOnlyContainer<T>::~ThreadSafeAddOnlyContainer() { | ||
Node const* node = front_.load(); | ||
while (node) { | ||
Node const* next = node->next(); | ||
delete node; | ||
node = next; | ||
} | ||
} | ||
|
||
template <typename T> | ||
template <typename... Args> | ||
T* ThreadSafeAddOnlyContainer<T>::makeAndHold(Args&&... args) { | ||
Node* expected = front_.load(); | ||
Node* newNode = new Node(expected, std::forward<Args>(args)...); | ||
while (!front_.compare_exchange_strong(expected, newNode)) { | ||
// another thread changed front_ before us so try again | ||
newNode->setNext(expected); | ||
} | ||
return newNode->address(); | ||
} | ||
|
||
template <typename T> | ||
template <typename... Args> | ||
ThreadSafeAddOnlyContainer<T>::Node::Node(Node* iNext, Args&&... args) : | ||
next_(iNext), | ||
data_(std::forward<Args>(args)...) { | ||
} | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#include "FWCore/Concurrency/interface/ThreadSafeAddOnlyContainer.h" | ||
|
||
#include <iostream> | ||
#include <string> | ||
|
||
namespace { | ||
template <typename U, typename V> | ||
class X { | ||
public: | ||
X(U const& a, V const& b, double c) : a_(a), b_(b), c_(c) { | ||
std::cout << "Constructing " << a_ << std::endl; | ||
} | ||
~X() { std::cout << "~X " << a_ << std::endl; } | ||
U a_; | ||
V b_; | ||
double c_; | ||
}; | ||
|
||
class Y { | ||
public: | ||
Y() { std::cout << "constructing Y" << std::endl; } | ||
~Y() { std::cout << "~Y" << std::endl; } | ||
}; | ||
} | ||
|
||
int main() { | ||
|
||
edm::ThreadSafeAddOnlyContainer<int> container1; | ||
int* ptr1 = container1.makeAndHold(11); | ||
// std::cout << *ptr1 << std::endl; | ||
if (*ptr1 != 11) abort(); | ||
|
||
edm::ThreadSafeAddOnlyContainer<X<std::string,int> > container2; | ||
X<std::string, int>* ptr2 = container2.makeAndHold(std::string("FOO"), 11, 21.0); | ||
// std::cout << ptr2->a_ << " " << ptr2->b_ << " " << ptr2->c_ << std::endl; | ||
if (ptr2->a_ != "FOO" || ptr2->b_ != 11 || ptr2->c_ != 21.0) abort(); | ||
|
||
|
||
X<std::string, int>* ptr3 = container2.makeAndHold(std::string("BAR"), 111, 121.0); | ||
// std::cout << ptr3->a_ << " " << ptr3->b_ << " " << ptr3->c_ << std::endl; | ||
if (ptr3->a_ != "BAR" || ptr3->b_ != 111 || ptr3->c_ != 121.0) abort(); | ||
|
||
edm::ThreadSafeAddOnlyContainer<X<std::string,int> > container3; | ||
|
||
edm::ThreadSafeAddOnlyContainer<Y> container4; | ||
container4.makeAndHold(); | ||
container4.makeAndHold(); | ||
|
||
return 0; | ||
} |