FastJet 3.4.1
thread_safety_helpers.hh
1//FJSTARTHEADER
2// $Id$
3//
4// Copyright (c) 2014-2023, Matteo Cacciari, Gavin P. Salam and Gregory Soyez
5//
6//----------------------------------------------------------------------
7// This file is part of FastJet.
8//
9// FastJet is free software; you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation; either version 2 of the License, or
12// (at your option) any later version.
13//
14// The algorithms that underlie FastJet have required considerable
15// development. They are described in the original FastJet paper,
16// hep-ph/0512210 and in the manual, arXiv:1111.6097. If you use
17// FastJet as part of work towards a scientific publication, please
18// quote the version you use and include a citation to the manual and
19// optionally also to hep-ph/0512210.
20//
21// FastJet is distributed in the hope that it will be useful,
22// but WITHOUT ANY WARRANTY; without even the implied warranty of
23// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24// GNU General Public License for more details.
25//
26// You should have received a copy of the GNU General Public License
27// along with FastJet. If not, see <http://www.gnu.org/licenses/>.
28//----------------------------------------------------------------------
29//FJENDHEADER
30
31#ifndef __FASTJET_THREAD_SAFETY_HELPERS_HH__
32#define __FASTJET_THREAD_SAFETY_HELPERS_HH__
33
34/// The code in this file is supposed to help writing code that will
35/// automatically provide thread-safe features when available and come
36/// revert back to "old/standard" C++ if thread-safety is not switched
37/// on
38///
39///\TODO fix doxygen comments (declare things as internal; make sure
40/// doxygen doc is not duplicate --- if necessary, keep only doxygen
41/// comments in the thread-safe versions)
42
43#include "fastjet/internal/base.hh"
44#include "fastjet/config.h"
45#include <limits>
46
47#ifdef FASTJET_HAVE_LIMITED_THREAD_SAFETY
48
49// introduces a few tools in CXX11 that we'll use in some FJ classes
50#include <atomic>
51
52FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
53
54namespace thread_safety_helpers{
55
56 //----------------------------------------------------------------------
57 /// \if internal_doc
58 /// \class AtomicCounter
59 ///
60 /// provides a thread-safe counter which can only step one unit at a time
61 /// and has overflow protection
62 /// \endif
63 template<typename T>
64 class AtomicCounter{
65 public:
66 /// default ctor
67 AtomicCounter() : _count{0}{}
68
69 /// ctor with initialisation
70 AtomicCounter(const T &count) : _count{count}{}
71
72 /// copy ctor (works around the deleted copy in atomic, see
73 /// e.g. http://stackoverflow.com/questions/19883092/error-implicitly-deleted-because-the-default-definition-would-be-ill-formed-ve)
74 AtomicCounter(const AtomicCounter &other) : _count{other._count.load()}{}
75
76 /// for a more friendly usage, overload the type cast to the
77 /// base template type
78 operator T() const{ return _count.load();}
79
80 /// get the count
81 T get() const{ return _count.load();}
82
83 /// set the counter to a given value
84 void set(const T new_value){
85 _count.store(new_value);
86 }
87
88 /// step the counter and return the count just before it was stepped
89 ///
90 /// Q: can we declare this as T && ...?
91 T step(){
92 // another thread could be upadting this at the same time, so extra
93 // care is needed.
94 //
95 // Recall that the compare_exchange_strong will return true if the
96 // exchange has been done. Otherwise, it means that the count
97 // changed in the meantime, so we try again. Also, since when it
98 // "fails" compare_exchange_strong loads the count of *this in
99 // expected, count does not need to be re-read in the loop!
100 //
101 // Note that at the end of this procedure, count will countain the
102 // number of times this warning occured just before this
103 // occurence. It can thus be used to see if it needs to be printed
104 // out
105 //
106 // Note also that compared to the apparently simpler fetch_add,
107 // this method also avoids overflows
108 T count = _count;
109 while (_count < std::numeric_limits<T>::max()
110 && !(_count.compare_exchange_strong(count, count+1)));
111 return count;
112 }
113
114 /// override the ++ operator
115 /// prefix version
116 inline T operator++(){
117 return step()+1;
118 }
119
120 /// override the ++ operator
121 /// postfix version
122 inline T operator++(int){
123 return step();
124 }
125
126 private:
127 std::atomic<T> _count; ///< the actual count
128 };
129
130 //----------------------------------------------------------------------
131 /// \if internal_doc
132 /// \class FirstTimeTrue
133 /// provides an object wich will return "true" the first time () is
134 /// called and false afterwards
135 /// \endif
136 class FirstTimeTrue{
137 public:
138 FirstTimeTrue(): _first_time{true}{}
139 // explicit copy ctor (this class contains atimoc vars)
140 FirstTimeTrue(const FirstTimeTrue &other) : _first_time{other._first_time.load()}{}
141 bool operator()(){
142 // Thread-safety note:
143 // the construct
144 // if (!_first_time) {return;}
145 // _first_time = false;
146 // is dangerous because the test can be passed by a second thread
147 // before the first one has set it to false. Use atomic exchange
148 // to handle this better
149 bool expected = true;
150 // this behaves as follows: if we have the expected value (true),
151 // set _first_time to the desired (false) and return
152 // true. Otherwise, do nothing and return false
153 //
154 // Note that since we are not using the "expected" value
155 // afterwards, we can use a relaxed memory ordering if the next
156 // call returns false
157 return _first_time.compare_exchange_strong(expected, false,
158 std::memory_order_seq_cst,
159 std::memory_order_relaxed);
160 }
161 private:
162 std::atomic<bool> _first_time;
163 };
164
165} // namespace thread_safety_helpers
166
167FASTJET_END_NAMESPACE
168
169#else // FJ wo thread-safety features
170
171FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
172
173namespace thread_safety_helpers{
174 //----------------------------------------------------------------------
175 /// \class AtomicCounter
176 ///
177 /// (would) provides a thread-safe counter (with CXX11 features)
178 template<typename T>
180 public:
181 /// default ctor
182 AtomicCounter() : _count(0){}
183
184 /// ctor with initialisation
185 AtomicCounter(const T &count) : _count(count){}
186
187 /// copy ctor
188 AtomicCounter(const AtomicCounter &other) : _count(other._count){}
189
190 /// for a more friendly usage, overload the type cast
191 ///
192 /// This will (likely) allow a transparent usage w or wo C++11
193 /// features enabled
194 operator T() const{ return _count;}
195
196 /// get the count
197 T get() const{ return _count;}
198
199 /// set the counter to a given value
200 void set(const T new_value){
201 _count = new_value;
202 }
203
204 /// step the counter and return the value just before it was stepped
205 T step(){
206 unsigned int count = _count;
207 if (_count < std::numeric_limits<T>::max()){ _count++; }
208 return count;
209 }
210
211 /// override the ++ operator
212 /// prefix version
213 inline T operator++(){
214 return step()+1;
215 }
216
217 /// override the ++ operator
218 /// postfix version
219 inline T operator++(int){
220 return step();
221 }
222
223 private:
224 T _count; ///< the actual value
225 };
226
227 //----------------------------------------------------------------------
228 /// \class FirstTimeTrue
229 /// provides an object wich will return "true" the first time () is
230 /// called and false afterwards
232 public:
233 FirstTimeTrue(): _first_time(true){}
234 bool operator()(){
235 if (!_first_time) {return false;}
236 _first_time = false;
237 return true;
238 }
239 private:
240 bool _first_time;
241 };
242} // namespace thread_safety_helpers
243
244FASTJET_END_NAMESPACE
245
246
247
248#endif // FASTJET_HAVE_LIMITED_THREAD_SAFETY
249
250#endif // __FASTJET_THREAD_SAFETY_HELPERS_HH__
(would) provides a thread-safe counter (with CXX11 features)
AtomicCounter(const AtomicCounter &other)
copy ctor
void set(const T new_value)
set the counter to a given value
T step()
step the counter and return the value just before it was stepped
T operator++()
override the ++ operator prefix version
T operator++(int)
override the ++ operator postfix version
AtomicCounter(const T &count)
ctor with initialisation
provides an object wich will return "true" the first time () is called and false afterwards