PMDK C++ bindings  1.9.1
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 <thread>
51 #include <unordered_map>
52 
53 namespace pmem
54 {
55 namespace detail
56 {
57 
64 struct id_manager {
65  id_manager();
66 
67  id_manager(const id_manager &) = delete;
68  id_manager &operator=(const id_manager &) = delete;
69 
70  size_t get();
71  void release(size_t id);
72 
73 private:
74  static constexpr size_t initial_queue_capacity = 1024;
75 
76  std::mutex mutex;
77  std::size_t queue_capacity;
78  std::deque<size_t> queue;
79 };
80 
84 
85  thread_id_type(const thread_id_type &) = delete;
86  thread_id_type &operator=(const thread_id_type &) = delete;
87 
89 
90  size_t get();
91 
92 private:
93  size_t id;
94 
95  static id_manager &get_id_manager();
96 };
97 
116 template <typename T, typename Mutex = obj::shared_mutex,
117  typename Storage =
120  using storage_type = Storage;
121  using mutex_type = Mutex;
122 
123 public:
124  /* traits */
125  using value_type = T;
126  using size_type = typename storage_type::size_type;
127  using difference_type = typename storage_type::difference_type;
128  using reference = value_type &;
129  using const_reference = const value_type &;
130  using pointer = value_type *;
131  using const_pointer = const value_type *;
132  using iterator = typename storage_type::iterator;
133  using const_iterator = typename storage_type::const_iterator;
134 
135  /* initialization */
136  template <typename Handler>
137  void initialize(Handler handler = [](reference) {});
138 
139  /* ctors & dtor */
141  ~enumerable_thread_specific() = default;
142 
143  /* access */
144  reference local();
145 
146  /* size */
147  bool empty() const;
148  void clear();
149  size_type size() const;
150 
151  /* iterators */
152  iterator begin();
153  iterator end();
154  const_iterator begin() const;
155  const_iterator end() const;
156 
157 private:
158  /* private helper methods */
159  obj::pool_base get_pool() const noexcept;
160  void set_cached_size(size_t s);
161  size_t get_cached_size();
162 
163  mutex_type _mutex;
164  storage_type _storage;
165 
166  obj::p<std::atomic<size_t>> _storage_size;
167 };
168 
169 inline id_manager::id_manager()
170  : queue_capacity(initial_queue_capacity), queue(initial_queue_capacity, 0)
171 {
172  /* Convert 0, 0, 0, ..., 0 into 0, 1, 2, ..., N */
173  std::iota(queue.begin(), queue.end(), 0);
174 }
175 
181 inline size_t
183 {
184  std::unique_lock<std::mutex> lock(mutex);
185 
186  if (queue.empty())
187  queue.push_front(queue_capacity++);
188 
189  auto front = queue.front();
190  queue.pop_front();
191 
192  return front;
193 }
194 
200 inline void
202 {
203  std::unique_lock<std::mutex> lock(mutex);
204 
205  queue.push_front(id);
206 }
207 
211 inline id_manager &
213 {
214  static id_manager manager;
215  return manager;
216 }
217 
224 {
225  auto &manager = get_id_manager();
226 
227  /*
228  * Drd had a bug related to static object initialization and reported
229  * conflicting load on std::mutex access inside id_manager.
230  */
231 #if LIBPMEMOBJ_CPP_VG_DRD_ENABLED
232  ANNOTATE_BENIGN_RACE_SIZED(
233  &manager, sizeof(std::mutex),
234  "https://bugs.kde.org/show_bug.cgi?id=416286");
235 #endif
236 
237  id = manager.get();
238 }
239 
246 {
247  get_id_manager().release(id);
248 }
249 
253 inline size_t
255 {
256  return id;
257 }
258 
262 template <typename T, typename Mutex, typename Storage>
264 {
265  _storage_size.get_rw() = 0;
266 }
267 
271 template <typename T, typename Mutex, typename Storage>
272 void
274 {
275  auto pop = get_pool();
276 
277  /* Helgrind does not understand std::atomic */
278 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
279  VALGRIND_HG_DISABLE_CHECKING(&_storage_size, sizeof(_storage_size));
280 #endif
281 
282 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED || LIBPMEMOBJ_CPP_VG_DRD_ENABLED
283  ANNOTATE_HAPPENS_BEFORE(&_storage_size);
284 #endif
285 
286  _storage_size.get_rw().store(s);
287  pop.persist(_storage_size);
288 }
289 
293 template <typename T, typename Mutex, typename Storage>
294 size_t
296 {
297  auto s = _storage_size.get_ro().load();
298 
299 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED || LIBPMEMOBJ_CPP_VG_DRD_ENABLED
300  ANNOTATE_HAPPENS_AFTER(&_storage_size);
301 #endif
302 
303  return s;
304 }
305 
313 template <typename T, typename Mutex, typename Storage>
314 template <typename Handler>
315 void
317 {
318  for (reference e : *this) {
319  handler(e);
320  }
321  clear();
322 }
323 
332 template <typename T, typename Mutex, typename Storage>
333 typename enumerable_thread_specific<T, Mutex, Storage>::reference
335 {
336  assert(pmemobj_tx_stage() != TX_STAGE_WORK);
337 
338  static thread_local thread_id_type tid;
339  auto index = tid.get();
340 
341  auto cached_size = get_cached_size();
342 
343  if (index >= cached_size) {
344  std::unique_lock<mutex_type> lock(_mutex);
345 
346  /* Size of the storage could have changed before we obtained the
347  * lock. That's why we read size once again. */
348  auto size = _storage.size();
349 
350  if (index >= size) {
351  _storage.resize(index + 1);
352  set_cached_size(index + 1);
353  } else if (size != cached_size) {
354  set_cached_size(size);
355  }
356  }
357 
358  /*
359  * Because _storage can only grow (unless clear() was called which
360  * should not happen simultaneously with this operation), index must be
361  * less than _storage.size().
362  */
363  return _storage[index];
364 }
365 
372 template <typename T, typename Mutex, typename Storage>
373 void
375 {
376  auto pop = get_pool();
377 
378  obj::transaction::run(pop, [&] {
379  _storage_size.get_rw() = 0;
380  _storage.clear();
381  });
382 }
383 
389 template <typename T, typename Mutex, typename Storage>
390 typename enumerable_thread_specific<T, Mutex, Storage>::size_type
392 {
393  return _storage.size();
394 }
395 
401 template <typename T, typename Mutex, typename Storage>
402 bool
404 {
405  return _storage.size() == 0;
406 }
407 
413 template <typename T, typename Mutex, typename Storage>
414 typename enumerable_thread_specific<T, Mutex, Storage>::iterator
416 {
417  return _storage.begin();
418 }
419 
425 template <typename T, typename Mutex, typename Storage>
426 typename enumerable_thread_specific<T, Mutex, Storage>::iterator
428 {
429  return _storage.end();
430 }
431 
437 template <typename T, typename Mutex, typename Storage>
438 typename enumerable_thread_specific<T, Mutex, Storage>::const_iterator
440 {
441  return _storage.begin();
442 }
443 
449 template <typename T, typename Mutex, typename Storage>
450 typename enumerable_thread_specific<T, Mutex, Storage>::const_iterator
452 {
453  return _storage.end();
454 }
455 
463 template <typename T, typename Mutex, typename Storage>
466 {
467  auto pop = pmemobj_pool_by_ptr(this);
468  assert(pop != nullptr);
469  return obj::pool_base(pop);
470 }
471 
472 } /* namespace detail */
473 } /* namespace pmem */
474 
475 #endif
pmem::detail::enumerable_thread_specific
Class for storing thread local data.
Definition: enumerable_thread_specific.hpp:119
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:391
pmem::detail::thread_id_type::get
size_t get()
Obtain current thread id.
Definition: enumerable_thread_specific.hpp:254
pmem::detail::enumerable_thread_specific::end
iterator end()
Returns an iterator to element after the last.
Definition: enumerable_thread_specific.hpp:427
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:44
pmem::detail::id_manager::get
size_t get()
Obtain unique thread id.
Definition: enumerable_thread_specific.hpp:182
common.hpp
Commonly used functionality.
pmem::detail::enumerable_thread_specific::get_pool
obj::pool_base get_pool() const noexcept
Private helper function.
Definition: enumerable_thread_specific.hpp:465
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:212
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:64
pmem::detail::thread_id_type::~thread_id_type
~thread_id_type()
thread_id_type destructor.
Definition: enumerable_thread_specific.hpp:245
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::enumerable_thread_specific::begin
iterator begin()
Returns an iterator to the beginning.
Definition: enumerable_thread_specific.hpp:415
pmem::detail::thread_id_type
RAII-style structure for holding thread id.
Definition: enumerable_thread_specific.hpp:82
pmem::detail::enumerable_thread_specific::local
reference local()
Returns data reference for the current thread.
Definition: enumerable_thread_specific.hpp:334
shared_mutex.hpp
Pmem-resident shared mutex.
segment_vector.hpp
A persistent version of segment vector implementation.
pmem::detail::enumerable_thread_specific::initialize
void initialize(Handler handler=[](reference) {})
Initialization method.
Definition: enumerable_thread_specific.hpp:316
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:295
pmem::detail::enumerable_thread_specific::empty
bool empty() const
Determines if container is empty or not.
Definition: enumerable_thread_specific.hpp:403
pmem::detail::thread_id_type::thread_id_type
thread_id_type()
thread_id_type constructor.
Definition: enumerable_thread_specific.hpp:223
pmem::detail::enumerable_thread_specific::clear
void clear()
Removes all elements from the container.
Definition: enumerable_thread_specific.hpp:374
pmem::detail::enumerable_thread_specific::enumerable_thread_specific
enumerable_thread_specific()
Constructor.
Definition: enumerable_thread_specific.hpp:263
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:273
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:201
mutex.hpp
Pmem-resident mutex.