vtkSMPThreadLocal.h.in 5.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
 /*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkSMPThreadLocal.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
// .NAME vtkSMPThreadLocal - A TBB based thread local storage implementation.
// .SECTION Description
// A thread local object is one that maintains a copy of an object of the
// template type for each thread that processes data. vtkSMPThreadLocal
// creates storage for all threads but the actual objects are created
// the first time Local() is called. Note that some of the vtkSMPThreadLocal
// API is not thread safe. It can be safely used in a multi-threaded
// environment because Local() returns storage specific to a particular
// thread, which by default will be accessed sequentially. It is also
// thread-safe to iterate over vtkSMPThreadLocal as long as each thread
// creates its own iterator and does not change any of the thread local
// objects.
//
// A common design pattern in using a thread local storage object is to
// write/accumulate data to local object when executing in parallel and
// then having a sequential code block that iterates over the whole storage
// using the iterators to do the final accumulation.
//
// .SECTION Warning
// There is absolutely no guarantee to the order in which the local objects
// will be stored and hence the order in which they will be traversed when
// using iterators. You should not even assume that two vtkSMPThreadLocal
// populated in the same parallel section will be populated in the same
// order. For example, consider the following
// \verbatim
// vtkSMPThreadLocal<int> Foo;
// vtkSMPThreadLocal<int> Bar;
// class AFunctor
// {
//    void Initialize() const
//    {
//        int& foo = Foo.Local();
//        int& bar = Bar.Local();
//        foo = random();
//        bar = foo;
//    }
//
//    void operator()(vtkIdType, vtkIdType) const
//    {}
// };
//
// AFunctor functor;
// vtkParalllelUtilities::For(0, 100000, functor);
//
// vtkSMPThreadLocal<int>::iterator itr1 = Foo.begin();
// vtkSMPThreadLocal<int>::iterator itr2 = Bar.begin();
// while (itr1 != Foo.end())
// {
//   assert(*itr1 == *itr2);
//   ++itr1; ++itr2;
// }
// \endverbatim
//
// It is possible and likely that the assert() will fail using the TBB
// backend. So if you need to store values related to each other and
// iterate over them together, use a struct or class to group them together
// and use a thread local of that class.

73 74
#ifndef vtkSMPThreadLocal_h
#define vtkSMPThreadLocal_h
75

76
#if defined(_MSC_VER) && _MSC_VER <= 1700
Sujin Philip's avatar
Sujin Philip committed
77 78 79
#pragma warning(push)
#pragma warning(disable : 4345)
#endif
80
#include <tbb/enumerable_thread_specific.h>
81
#if defined(_MSC_VER) && _MSC_VER <= 1700
Sujin Philip's avatar
Sujin Philip committed
82 83
#pragma warning(pop)
#endif
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

template <typename T>
class vtkSMPThreadLocal
{
  typedef tbb::enumerable_thread_specific<T> TLS;
  typedef typename TLS::iterator TLSIter;
public:
  // Description:
  // Default constructor. Creates a default exemplar.
  vtkSMPThreadLocal()
    {
    }

  // Description:
  // Constructor that allows the specification of an exemplar object
  // which is used when constructing objects when Local() is first called.
  // Note that a copy of the exemplar is created using its copy constructor.
Sujin Philip's avatar
Sujin Philip committed
101
  explicit vtkSMPThreadLocal(const T& exemplar) : Internal(exemplar)
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    {
    }

  // Description:
  // Returns an object of type T that is local to the current thread.
  // This needs to be called mainly within a threaded execution path.
  // It will create a new object (local to the tread so each thread
  // get their own when calling Local) which is a copy of exemplar as passed
  // to the constructor (or a default object if no exemplar was provided)
  // the first time it is called. After the first time, it will return
  // the same object.
  T& Local()
    {
      return this->Internal.local();
    }

Sujin Philip's avatar
Sujin Philip committed
118 119 120 121 122 123 124
  // Description:
  // Return the number of thread local objects that have been initialized
  size_t size() const
    {
      return this->Internal.size();
    }

125 126
  // Description:
  // Subset of the standard iterator API.
Sujin Philip's avatar
Sujin Philip committed
127
  // The most common design pattern is to use iterators in a sequential
128 129 130 131 132 133 134 135 136 137 138 139 140 141
  // code block and to use only the thread local objects in parallel
  // code blocks.
  // It is thread safe to iterate over the thread local containers
  // as long as each thread uses its own iterator and does not modify
  // objects in the container.
  class iterator
  {
  public:
    iterator& operator++()
      {
        ++this->Iter;
        return *this;
      }

Sujin Philip's avatar
Sujin Philip committed
142 143 144 145 146 147 148 149 150 151 152 153
    iterator operator++(int)
      {
        iterator copy = *this;
        ++this->Iter;
        return copy;
      }

    bool operator==(const iterator& other)
      {
        return this->Iter == other.Iter;
      }

154 155 156 157 158 159 160 161 162 163
    bool operator!=(const iterator& other)
      {
        return this->Iter != other.Iter;
      }

    T& operator*()
      {
        return *this->Iter;
      }

Sujin Philip's avatar
Sujin Philip committed
164 165 166 167 168
    T* operator->()
      {
        return &*this->Iter;
      }

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
  private:
    TLSIter Iter;

    friend class vtkSMPThreadLocal<T>;
  };

  // Description:
  // Returns a new iterator pointing to the beginning of
  // the local storage container. Thread safe.
  iterator begin()
    {
      iterator iter;
      iter.Iter = this->Internal.begin();
      return iter;
    };

  // Description:
  // Returns a new iterator pointing to past the end of
  // the local storage container. Thread safe.
  iterator end()
    {
      iterator iter;
      iter.Iter = this->Internal.end();
      return iter;
    }

private:
  TLS Internal;
Sujin Philip's avatar
Sujin Philip committed
197 198 199 200

  // disable copying
  vtkSMPThreadLocal(const vtkSMPThreadLocal&);
  void operator=(const vtkSMPThreadLocal&);
201 202 203
};
#endif
// VTK-HeaderTest-Exclude: vtkSMPThreadLocal.h