Skip to content

Commit

Permalink
Merge pull request #14213 from wddgit/threadSafeAddOnlyContainer
Browse files Browse the repository at this point in the history
Add new template class ThreadSafeAddOnlyContainer
  • Loading branch information
cmsbuild committed Apr 23, 2016
2 parents fed4d3b + 47177c6 commit 6510bb0
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 1 deletion.
96 changes: 96 additions & 0 deletions FWCore/Concurrency/interface/ThreadSafeAddOnlyContainer.h
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
4 changes: 3 additions & 1 deletion FWCore/Concurrency/test/BuildFile.xml
Original file line number Diff line number Diff line change
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
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;
}

0 comments on commit 6510bb0

Please sign in to comment.