Loading...
Searching...
No Matches
thread_safety_helpers.hh
//FJSTARTHEADER
// $Id$
//
// Copyright (c) 2014-2021, Matteo Cacciari, Gavin P. Salam and Gregory Soyez
//
//----------------------------------------------------------------------
// This file is part of FastJet.
//
// FastJet is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// The algorithms that underlie FastJet have required considerable
// development. They are described in the original FastJet paper,
// hep-ph/0512210 and in the manual, arXiv:1111.6097. If you use
// FastJet as part of work towards a scientific publication, please
// quote the version you use and include a citation to the manual and
// optionally also to hep-ph/0512210.
//
// FastJet is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FastJet. If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------
//FJENDHEADER
#ifndef __FASTJET_THREAD_SAFETY_HELPERS_HH__
#define __FASTJET_THREAD_SAFETY_HELPERS_HH__
/// The code in this file is supposed to help writing code that will
/// automatically provide thread-safe features when available and come
/// revert back to "old/standard" C++ if thread-safety is not switched
/// on
///
///\TODO fix doxygen comments (declare things as internal; make sure
/// doxygen doc is not duplicate --- if necessary, keep only doxygen
/// comments in the thread-safe versions)
#include "fastjet/internal/base.hh"
#include "fastjet/config.h"
#include <limits>
#ifdef FASTJET_HAVE_LIMITED_THREAD_SAFETY
// introduces a few tools in CXX11 that we'll use in some FJ classes
#include <atomic>
FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
namespace thread_safety_helpers{
//----------------------------------------------------------------------
/// \if internal_doc
/// \class AtomicCounter
///
/// provides a thread-safe counter which can only step one unit at a time
/// and has overflow protection
/// \endif
template<typename T>
class AtomicCounter{
public:
/// default ctor
AtomicCounter() : _count{0}{}
/// ctor with initialisation
AtomicCounter(const T &count) : _count{count}{}
/// copy ctor (works around the deleted copy in atomic, see
/// e.g. http://stackoverflow.com/questions/19883092/error-implicitly-deleted-because-the-default-definition-would-be-ill-formed-ve)
AtomicCounter(const AtomicCounter &other) : _count{other._count.load()}{}
/// for a more friendly usage, overload the type cast to the
/// base template type
operator T() const{ return _count.load();}
/// get the count
T get() const{ return _count.load();}
/// set the counter to a given value
void set(const T new_value){
_count.store(new_value);
}
/// step the counter and return the count just before it was stepped
///
/// Q: can we declare this as T && ...?
T step(){
// another thread could be upadting this at the same time, so extra
// care is needed.
//
// Recall that the compare_exchange_strong will return true if the
// exchange has been done. Otherwise, it means that the count
// changed in the meantime, so we try again. Also, since when it
// "fails" compare_exchange_strong loads the count of *this in
// expected, count does not need to be re-read in the loop!
//
// Note that at the end of this procedure, count will countain the
// number of times this warning occured just before this
// occurence. It can thus be used to see if it needs to be printed
// out
//
// Note also that compared to the apparently simpler fetch_add,
// this method also avoids overflows
T count = _count;
while (_count < std::numeric_limits<T>::max()
&& !(_count.compare_exchange_strong(count, count+1)));
return count;
}
/// override the ++ operator
/// prefix version
inline T operator++(){
return step()+1;
}
/// override the ++ operator
/// postfix version
inline T operator++(int){
return step();
}
private:
std::atomic<T> _count; ///< the actual count
};
//----------------------------------------------------------------------
/// \if internal_doc
/// \class FirstTimeTrue
/// provides an object wich will return "true" the first time () is
/// called and false afterwards
/// \endif
class FirstTimeTrue{
public:
FirstTimeTrue(): _first_time{true}{}
// explicit copy ctor (this class contains atimoc vars)
FirstTimeTrue(const FirstTimeTrue &other) : _first_time{other._first_time.load()}{}
bool operator()(){
// Thread-safety note:
// the construct
// if (!_first_time) {return;}
// _first_time = false;
// is dangerous because the test can be passed by a second thread
// before the first one has set it to false. Use atomic exchange
// to handle this better
bool expected = true;
// this behaves as follows: if we have the expected value (true),
// set _first_time to the desired (false) and return
// true. Otherwise, do nothing and return false
//
// Note that since we are not using the "expected" value
// afterwards, we can use a relaxed memory ordering if the next
// call returns false
return _first_time.compare_exchange_strong(expected, false,
std::memory_order_seq_cst,
std::memory_order_relaxed);
}
private:
std::atomic<bool> _first_time;
};
} // namespace thread_safety_helpers
FASTJET_END_NAMESPACE
#else // FJ wo thread-safety features
FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
namespace thread_safety_helpers{
//----------------------------------------------------------------------
/// \class AtomicCounter
///
/// (would) provides a thread-safe counter (with CXX11 features)
template<typename T>
class AtomicCounter{
public:
/// default ctor
AtomicCounter() : _count(0){}
/// ctor with initialisation
AtomicCounter(const T &count) : _count(count){}
/// copy ctor
AtomicCounter(const AtomicCounter &other) : _count(other._count){}
/// for a more friendly usage, overload the type cast
///
/// This will (likely) allow a transparent usage w or wo C++11
/// features enabled
operator T() const{ return _count;}
/// get the count
T get() const{ return _count;}
/// set the counter to a given value
void set(const T new_value){
_count = new_value;
}
/// step the counter and return the value just before it was stepped
T step(){
unsigned int count = _count;
if (_count < std::numeric_limits<T>::max()){ _count++; }
return count;
}
/// override the ++ operator
/// prefix version
inline T operator++(){
return step()+1;
}
/// override the ++ operator
/// postfix version
inline T operator++(int){
return step();
}
private:
T _count; ///< the actual value
};
//----------------------------------------------------------------------
/// \class FirstTimeTrue
/// provides an object wich will return "true" the first time () is
/// called and false afterwards
class FirstTimeTrue{
public:
FirstTimeTrue(): _first_time(true){}
bool operator()(){
if (!_first_time) {return false;}
_first_time = false;
return true;
}
private:
bool _first_time;
};
} // namespace thread_safety_helpers
FASTJET_END_NAMESPACE
#endif // FASTJET_HAVE_LIMITED_THREAD_SAFETY
#endif // __FASTJET_THREAD_SAFETY_HELPERS_HH__
fastjet::thread_safety_helpers::AtomicCounter::AtomicCounter
AtomicCounter()
default ctor
Definition thread_safety_helpers.hh:182
fastjet::thread_safety_helpers::AtomicCounter::AtomicCounter
AtomicCounter(const AtomicCounter &other)
copy ctor
Definition thread_safety_helpers.hh:188
fastjet::thread_safety_helpers::AtomicCounter::set
void set(const T new_value)
set the counter to a given value
Definition thread_safety_helpers.hh:200
fastjet::thread_safety_helpers::AtomicCounter::step
T step()
step the counter and return the value just before it was stepped
Definition thread_safety_helpers.hh:205
fastjet::thread_safety_helpers::AtomicCounter::operator++
T operator++()
override the ++ operator prefix version
Definition thread_safety_helpers.hh:213
fastjet::thread_safety_helpers::AtomicCounter::operator++
T operator++(int)
override the ++ operator postfix version
Definition thread_safety_helpers.hh:219
fastjet::thread_safety_helpers::AtomicCounter::AtomicCounter
AtomicCounter(const T &count)
ctor with initialisation
Definition thread_safety_helpers.hh:185
fastjet::thread_safety_helpers::AtomicCounter::get
T get() const
get the count
Definition thread_safety_helpers.hh:197