You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
6.4 KiB
C
230 lines
6.4 KiB
C
3 weeks ago
|
/*=========================================================================
|
||
|
|
||
|
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 simple thread local implementation for sequential operations.
|
||
|
// .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.
|
||
|
//
|
||
|
// Note that this particular implementation is designed to work in sequential
|
||
|
// mode and supports only 1 thread.
|
||
|
|
||
|
#ifndef vtkSMPThreadLocal_h
|
||
|
#define vtkSMPThreadLocal_h
|
||
|
|
||
|
#include "vtkSystemIncludes.h"
|
||
|
|
||
|
#include <iterator>
|
||
|
#include <vector>
|
||
|
|
||
|
template <typename T>
|
||
|
class vtkSMPThreadLocal
|
||
|
{
|
||
|
typedef std::vector<T> TLS;
|
||
|
typedef typename TLS::iterator TLSIter;
|
||
|
public:
|
||
|
// Description:
|
||
|
// Default constructor. Creates a default exemplar.
|
||
|
vtkSMPThreadLocal() : NumInitialized(0)
|
||
|
{
|
||
|
this->Initialize();
|
||
|
}
|
||
|
|
||
|
// 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.
|
||
|
explicit vtkSMPThreadLocal(const T& exemplar)
|
||
|
: NumInitialized(0), Exemplar(exemplar)
|
||
|
{
|
||
|
this->Initialize();
|
||
|
}
|
||
|
|
||
|
// 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 thread 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()
|
||
|
{
|
||
|
int tid = this->GetThreadID();
|
||
|
if (!this->Initialized[tid])
|
||
|
{
|
||
|
this->Internal[tid] = this->Exemplar;
|
||
|
this->Initialized[tid] = true;
|
||
|
++this->NumInitialized;
|
||
|
}
|
||
|
return this->Internal[tid];
|
||
|
}
|
||
|
|
||
|
// Description:
|
||
|
// Return the number of thread local objects that have been initialized
|
||
|
size_t size() const
|
||
|
{
|
||
|
return this->NumInitialized;
|
||
|
}
|
||
|
|
||
|
// Description:
|
||
|
// Subset of the standard iterator API.
|
||
|
// The most common design pattern is to use iterators in a sequential
|
||
|
// 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 std::iterator<std::forward_iterator_tag, T> // for iterator_traits
|
||
|
{
|
||
|
public:
|
||
|
iterator& operator++()
|
||
|
{
|
||
|
this->InitIter++;
|
||
|
this->Iter++;
|
||
|
|
||
|
// Make sure to skip uninitialized
|
||
|
// entries.
|
||
|
while(this->InitIter != this->EndIter)
|
||
|
{
|
||
|
if (*this->InitIter)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
this->InitIter++;
|
||
|
this->Iter++;
|
||
|
}
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
iterator operator++(int)
|
||
|
{
|
||
|
iterator copy = *this;
|
||
|
++(*this);
|
||
|
return copy;
|
||
|
}
|
||
|
|
||
|
bool operator==(const iterator& other)
|
||
|
{
|
||
|
return this->Iter == other.Iter;
|
||
|
}
|
||
|
|
||
|
bool operator!=(const iterator& other)
|
||
|
{
|
||
|
return this->Iter != other.Iter;
|
||
|
}
|
||
|
|
||
|
T& operator*()
|
||
|
{
|
||
|
return *this->Iter;
|
||
|
}
|
||
|
|
||
|
T* operator->()
|
||
|
{
|
||
|
return &*this->Iter;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
friend class vtkSMPThreadLocal<T>;
|
||
|
std::vector<bool>::iterator InitIter;
|
||
|
std::vector<bool>::iterator EndIter;
|
||
|
TLSIter Iter;
|
||
|
};
|
||
|
|
||
|
// Description:
|
||
|
// Returns a new iterator pointing to the beginning of
|
||
|
// the local storage container. Thread safe.
|
||
|
iterator begin()
|
||
|
{
|
||
|
TLSIter iter = this->Internal.begin();
|
||
|
std::vector<bool>::iterator iter2 =
|
||
|
this->Initialized.begin();
|
||
|
std::vector<bool>::iterator enditer =
|
||
|
this->Initialized.end();
|
||
|
// fast forward to first initialized
|
||
|
// value
|
||
|
while(iter2 != enditer)
|
||
|
{
|
||
|
if (*iter2)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
iter2++;
|
||
|
iter++;
|
||
|
}
|
||
|
iterator retVal;
|
||
|
retVal.InitIter = iter2;
|
||
|
retVal.EndIter = enditer;
|
||
|
retVal.Iter = iter;
|
||
|
return retVal;
|
||
|
};
|
||
|
|
||
|
// Description:
|
||
|
// Returns a new iterator pointing to past the end of
|
||
|
// the local storage container. Thread safe.
|
||
|
iterator end()
|
||
|
{
|
||
|
iterator retVal;
|
||
|
retVal.InitIter = this->Initialized.end();
|
||
|
retVal.EndIter = this->Initialized.end();
|
||
|
retVal.Iter = this->Internal.end();
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
TLS Internal;
|
||
|
std::vector<bool> Initialized;
|
||
|
size_t NumInitialized;
|
||
|
T Exemplar;
|
||
|
|
||
|
void Initialize()
|
||
|
{
|
||
|
this->Internal.resize(this->GetNumberOfThreads());
|
||
|
this->Initialized.resize(this->GetNumberOfThreads());
|
||
|
std::fill(this->Initialized.begin(),
|
||
|
this->Initialized.end(),
|
||
|
false);
|
||
|
}
|
||
|
|
||
|
inline int GetNumberOfThreads()
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
inline int GetThreadID()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// disable copying
|
||
|
vtkSMPThreadLocal(const vtkSMPThreadLocal&);
|
||
|
void operator=(const vtkSMPThreadLocal&);
|
||
|
};
|
||
|
#endif
|
||
|
// VTK-HeaderTest-Exclude: vtkSMPThreadLocal.h
|