PMDK C++ bindings  1.9
This is the C++ bindings documentation for PMDK's libpmemobj.
enumerable_thread_specific.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2019-2020, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
38 #ifndef LIBPMEMOBJ_CPP_ENUMERABLE_THREAD_SPECIFIC_HPP
39 #define LIBPMEMOBJ_CPP_ENUMERABLE_THREAD_SPECIFIC_HPP
40 
43 #include <libpmemobj++/mutex.hpp>
45 
46 #include <cassert>
47 #include <deque>
48 #include <mutex>
49 #include <numeric>
50 #include <shared_mutex>
51 #include <thread>
52 #include <unordered_map>
53 
54 namespace pmem
55 {
56 namespace detail
57 {
58 
65 struct id_manager {
66  id_manager();
67 
68  id_manager(const id_manager &) = delete;
69  id_manager &operator=(const id_manager &) = delete;
70 
71  size_t get();
72  void release(size_t id);
73 
74 private:
75  static constexpr size_t initial_queue_capacity = 1024;
76 
77  std::mutex mutex;
78  std::size_t queue_capacity;
79  std::deque<size_t> queue;
80 };
81 
85 
86  thread_id_type(const thread_id_type &) = delete;
87  thread_id_type &operator=(const thread_id_type &) = delete;
88 
90 
91  size_t get();
92 
93 private:
94  size_t id;
95 
96  static id_manager &get_id_manager();
97 };
98 
117 template <typename T, typename Mutex = obj::shared_mutex,
118  typename Storage =
121  using storage_type = Storage;
122  using mutex_type = Mutex;
123 
124 public:
125  /* traits */
126  using value_type = T;
127  using size_type = typename storage_type::size_type;
128  using difference_type = typename storage_type::difference_type;
129  using reference = value_type &;
130  using const_reference = const value_type &;
131  using pointer = value_type *;
132  using const_pointer = const value_type *;
133  using iterator = typename storage_type::iterator;
134  using const_iterator = typename storage_type::const_iterator;
135 
136  /* initialization */
137  template <typename Handler>
138  void initialize(Handler handler = [](reference) {});
139 
140  /* ctors & dtor */
142  ~enumerable_thread_specific() = default;
143 
144  /* access */
145  reference local();
146 
147  /* size */
148  bool empty() const;
149  void clear();
150  size_type size() const;
151 
152  /* iterators */
153  iterator begin();
154  iterator end();
155  const_iterator begin() const;
156  const_iterator end() const;
157 
158 private:
159  /* private helper methods */
160  obj::pool_base get_pool() const noexcept;
161  void set_cached_size(size_t s);
162  size_t get_cached_size();
163 
164  mutex_type _mutex;
165  storage_type _storage;
166 
167  obj::p<std::atomic<size_t>> _storage_size;
168 };
169 
170 inline id_manager::id_manager()
171  : queue_capacity(initial_queue_capacity), queue(initial_queue_capacity, 0)
172 {
173  /* Convert 0, 0, 0, ..., 0 into 0, 1, 2, ..., N */
174  std::iota(queue.begin(), queue.end(), 0);
175 }
176 
182 inline size_t
184 {
185  std::unique_lock<std::mutex> lock(mutex);
186 
187  if (queue.empty())
188  queue.push_front(queue_capacity++);
189 
190  auto front = queue.front();
191  queue.pop_front();
192 
193  return front;
194 }
195 
201 inline void
203 {
204  std::unique_lock<std::mutex> lock(mutex);
205 
206  queue.push_front(id);
207 }
208 
212 inline id_manager &
214 {
215  static id_manager manager;
216  return manager;
217 }
218 
225 {
226  auto &manager = get_id_manager();
227 
228  /*
229  * Drd had a bug related to static object initialization and reported
230  * conflicting load on std::mutex access inside id_manager.
231  */
232 #if LIBPMEMOBJ_CPP_VG_DRD_ENABLED
233  ANNOTATE_BENIGN_RACE_SIZED(
234  &manager, sizeof(std::mutex),
235  "https://bugs.kde.org/show_bug.cgi?id=416286");
236 #endif
237 
238  id = manager.get();
239 }
240 
247 {
248  get_id_manager().release(id);
249 }
250 
254 inline size_t
256 {
257  return id;
258 }
259 
263 template <typename T, typename Mutex, typename Storage>
265 {
266  _storage_size.get_rw() = 0;
267 }
268 
272 template <typename T, typename Mutex, typename Storage>
273 void
275 {
276  auto pop = get_pool();
277 
278  /* Helgrind does not understand std::atomic */
279 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
280  VALGRIND_HG_DISABLE_CHECKING(&_storage_size, sizeof(_storage_size));
281 #endif
282 
283 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED || LIBPMEMOBJ_CPP_VG_DRD_ENABLED
284  ANNOTATE_HAPPENS_BEFORE(&_storage_size);
285 #endif
286 
287  _storage_size.get_rw().store(s);
288  pop.persist(_storage_size);
289 }
290 
294 template <typename T, typename Mutex, typename Storage>
295 size_t
297 {
298  auto s = _storage_size.get_ro().load();
299 
300 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED || LIBPMEMOBJ_CPP_VG_DRD_ENABLED
301  ANNOTATE_HAPPENS_AFTER(&_storage_size);
302 #endif
303 
304  return s;
305 }
306 
314 template <typename T, typename Mutex, typename Storage>
315 template <typename Handler>
316 void
318 {
319  for (reference e : *this) {
320  handler(e);
321  }
322  clear();
323 }
324 
333 template <typename T, typename Mutex, typename Storage>
334 typename enumerable_thread_specific<T, Mutex, Storage>::reference
336 {
337  assert(pmemobj_tx_stage() != TX_STAGE_WORK);
338 
339  static thread_local thread_id_type tid;
340  auto index = tid.get();
341 
342  auto cached_size = get_cached_size();
343 
344  if (index >= cached_size) {
345  std::unique_lock<mutex_type> lock(_mutex);
346 
347  /* Size of the storage could have changed before we obtained the
348  * lock. That's why we read size once again. */
349  auto size = _storage.size();
350 
351  if (index >= size) {
352  _storage.resize(index + 1);
353  set_cached_size(index + 1);
354  } else if (size != cached_size) {
355  set_cached_size(size);
356  }
357  }
358 
359  /*
360  * Because _storage can only grow (unless clear() was called which
361  * should not happen simultaneously with this operation), index must be
362  * less than _storage.size().
363  */
364  return _storage[index];
365 }
366 
373 template <typename T, typename Mutex, typename Storage>
374 void
376 {
377  auto pop = get_pool();
378 
379  obj::transaction::run(pop, [&] {
380  _storage_size.get_rw() = 0;
381  _storage.clear();
382  });
383 }
384 
390 template <typename T, typename Mutex, typename Storage>
391 typename enumerable_thread_specific<T, Mutex, Storage>::size_type
393 {
394  return _storage.size();
395 }
396 
402 template <typename T, typename Mutex, typename Storage>
403 bool
405 {
406  return _storage.size() == 0;
407 }
408 
414 template <typename T, typename Mutex, typename Storage>
415 typename enumerable_thread_specific<T, Mutex, Storage>::iterator
417 {
418  return _storage.begin();
419 }
420 
426 template <typename T, typename Mutex, typename Storage>
427 typename enumerable_thread_specific<T, Mutex, Storage>::iterator
429 {
430  return _storage.end();
431 }
432 
438 template <typename T, typename Mutex, typename Storage>
439 typename enumerable_thread_specific<T, Mutex, Storage>::const_iterator
441 {
442  return _storage.begin();
443 }
444 
450 template <typename T, typename Mutex, typename Storage>
451 typename enumerable_thread_specific<T, Mutex, Storage>::const_iterator
453 {
454  return _storage.end();
455 }
456 
464 template <typename T, typename Mutex, typename Storage>
465 obj::pool_base
467 {
468  auto pop = pmemobj_pool_by_ptr(this);
469  assert(pop != nullptr);
470  return obj::pool_base(pop);
471 }
472 
473 } /* namespace detail */
474 } /* namespace pmem */
475 
476 #endif
pmem::detail::enumerable_thread_specific
Class for storing thread local data.
Definition: enumerable_thread_specific.hpp:120
pmem::obj::segment_vector
Segment table is a data type with a vector-like interface The difference is that it does not do reall...
Definition: segment_vector.hpp:526
pmem::detail::enumerable_thread_specific::size
size_type size() const
Returns number of elements being stored in the container.
Definition: enumerable_thread_specific.hpp:392
pmem::detail::thread_id_type::get
size_t get()
Obtain current thread id.
Definition: enumerable_thread_specific.hpp:255
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:43
pmem::detail::id_manager::get
size_t get()
Obtain unique thread id.
Definition: enumerable_thread_specific.hpp:183
common.hpp
pmem::detail::enumerable_thread_specific::get_pool
obj::pool_base get_pool() const noexcept
Private helper function.
Definition: enumerable_thread_specific.hpp:466
pmem::detail::thread_id_type::get_id_manager
static id_manager & get_id_manager()
Get reference to id_manager instance.
Definition: enumerable_thread_specific.hpp:213
pmem::obj::p
Resides on pmem class.
Definition: p.hpp:64
pmem::detail::id_manager
This structure is used for assigning unique thread ids so that those ids will be reused in case of th...
Definition: enumerable_thread_specific.hpp:65
pmem::detail::thread_id_type::~thread_id_type
~thread_id_type()
thread_id_type destructor.
Definition: enumerable_thread_specific.hpp:246
pmem::detail::enumerable_thread_specific::end
iterator end()
Returns an iterator to element after the last.
Definition: enumerable_thread_specific.hpp:428
pmem::obj::transaction::run
static void run(pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:422
pmem::detail::thread_id_type
RAII-style structure for holding thread id.
Definition: enumerable_thread_specific.hpp:83
pmem::detail::enumerable_thread_specific::local
reference local()
Returns data reference for the current thread.
Definition: enumerable_thread_specific.hpp:335
shared_mutex.hpp
segment_vector.hpp
pmem::detail::enumerable_thread_specific::initialize
void initialize(Handler handler=[](reference) {})
Initialization method.
Definition: enumerable_thread_specific.hpp:317
pmem::detail::enumerable_thread_specific::get_cached_size
size_t get_cached_size()
Get cached storage size and make valgrind annotations.
Definition: enumerable_thread_specific.hpp:296
pmem::detail::enumerable_thread_specific::empty
bool empty() const
Determines if container is empty or not.
Definition: enumerable_thread_specific.hpp:404
pmem::detail::thread_id_type::thread_id_type
thread_id_type()
thread_id_type constructor.
Definition: enumerable_thread_specific.hpp:224
pmem::detail::enumerable_thread_specific::clear
void clear()
Removes all elements from the container.
Definition: enumerable_thread_specific.hpp:375
pmem::detail::enumerable_thread_specific::begin
iterator begin()
Returns an iterator to the beginning.
Definition: enumerable_thread_specific.hpp:416
pmem::detail::enumerable_thread_specific::enumerable_thread_specific
enumerable_thread_specific()
Constructor.
Definition: enumerable_thread_specific.hpp:264
pmem::detail::enumerable_thread_specific::set_cached_size
void set_cached_size(size_t s)
Set cached storage size, persist it and make valgrind annotations.
Definition: enumerable_thread_specific.hpp:274
pmem::obj::pool_base
The non-template pool base class.
Definition: pool.hpp:75
pmem::obj::shared_mutex
Persistent memory resident shared_mutex implementation.
Definition: shared_mutex.hpp:59
pmem::detail::id_manager::release
void release(size_t id)
Releases thread id so that it can be reused by other threads.
Definition: enumerable_thread_specific.hpp:202
mutex.hpp