FastJet  3.4.0
thread_safety_helpers.hh
1 //FJSTARTHEADER
2 // $Id$
3 //
4 // Copyright (c) 2014-2021, 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 
52 FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
53 
54 namespace 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 
167 FASTJET_END_NAMESPACE
168 
169 #else // FJ wo thread-safety features
170 
171 FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
172 
173 namespace 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 
244 FASTJET_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