Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new template class ThreadSafeAddOnlyContainer #14213

Merged
merged 1 commit into from Apr 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 96 additions & 0 deletions FWCore/Concurrency/interface/ThreadSafeAddOnlyContainer.h
@@ -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
4 changes: 3 additions & 1 deletion FWCore/Concurrency/test/BuildFile.xml
Expand Up @@ -5,4 +5,6 @@
<bin file="test_xercesInitialize.cpp">
<use name="FWCore/Concurrency"/>
</bin>

<bin file="ThreadSafeAddOnlyContainer_t.cpp">
<use name="FWCore/Concurrency"/>
</bin>
50 changes: 50 additions & 0 deletions FWCore/Concurrency/test/ThreadSafeAddOnlyContainer_t.cpp
@@ -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;
}