2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
12 #include <common/exception.hpp>
13 #include <common/hashtable/hashtable.hpp>
14 #include <common/macros.hpp>
19 #include <urcu/list.h>
20 #include <urcu/rculfhash.h>
27 * Wrapper around an urcu read lock which satisfies the 'Mutex' named
28 * requirements of C++11. Satisfying those requirements facilitates the use of
29 * standard concurrency support library facilities.
31 * read_lock is under the details namespace since it is unlikely to be used
32 * directly by exception-safe code. See read_lock_guard.
36 read_lock() = default;
37 ~read_lock() = default;
39 /* "Not copyable" and "not moveable" Mutex requirements. */
40 read_lock(const read_lock&) = delete;
41 read_lock(read_lock&&) = delete;
42 read_lock& operator=(read_lock&&) = delete;
43 read_lock& operator=(const read_lock&) = delete;
61 } /* namespace details */
64 * Provides the basic concept of std::lock_guard for rcu reader locks.
66 * The RCU reader lock is held for the duration of lock_guard's lifetime.
68 class read_lock_guard {
70 read_lock_guard() = default;
71 ~read_lock_guard() = default;
73 read_lock_guard(const read_lock_guard&) = delete;
74 read_lock_guard(read_lock_guard&&) = delete;
75 read_lock_guard& operator=(read_lock_guard&&) = delete;
76 read_lock_guard& operator=(const read_lock_guard&) = delete;
79 details::read_lock _lock;
80 std::lock_guard<details::read_lock> _guard{ _lock };
83 using unique_read_lock = std::unique_lock<details::read_lock>;
87 /* Implementation for types that contain a straight cds_lfht_node. */
88 template <typename ContainedType, typename NodeType, NodeType ContainedType::*Member>
89 static typename std::enable_if<std::is_same<cds_lfht_node, NodeType>::value, ContainedType *>::type
90 get_element_from_node(cds_lfht_node& node)
92 return lttng::utils::container_of(&node, Member);
95 /* Specialization for NodeType deriving from lttng_ht_node. */
96 template <typename ContainedType, typename NodeType, NodeType ContainedType::*Member>
97 static typename std::enable_if<std::is_base_of<lttng_ht_node, NodeType>::value, ContainedType *>::type
98 get_element_from_node(cds_lfht_node& node)
100 return lttng_ht_node_container_of(&node, Member);
102 } /* namespace details */
105 * The lfht_iteration_adapter class template wraps the liburcu lfht API to provide iteration
106 * capabilities. It allows users to iterate over a lock-free hash table with ranged-for semantics
107 * while holding the RCU read lock. The reader lock is held for the lifetime of the iteration
108 * adapter (i.e. not the lifetime of the iterators it provides).
110 template <typename ContainedType, typename NodeType, NodeType ContainedType::*Member>
111 class lfht_iteration_adapter {
113 /* Nested iterator class defines the iterator for lfht_iteration_adapter. */
114 class iterator : public std::iterator<std::input_iterator_tag, std::uint64_t> {
115 /* Allow lfht_iteration_adapter to access private members of iterator. */
116 friend lfht_iteration_adapter;
119 iterator(const iterator& other) = default;
120 iterator(iterator&& other) noexcept = default;
121 ~iterator() = default;
122 iterator& operator=(const iterator&) = delete;
123 iterator& operator=(iterator&&) noexcept = delete;
125 /* Move to the next element in the hash table. */
126 iterator& operator++()
128 cds_lfht_next(&_ht, &_it);
132 bool operator==(const iterator& other) const noexcept
134 /* Compare pointed nodes by address. */
135 return other._it.node == _it.node;
138 bool operator!=(const iterator& other) const noexcept
140 return !(*this == other);
143 /* Dereference the iterator to access the contained element. */
144 ContainedType *operator*() const
146 auto *node = _it.node;
148 /* Throw an exception if dereferencing an end iterator. */
150 LTTNG_THROW_OUT_OF_RANGE(
151 "Dereferenced an iterator at the end of a liburcu hashtable");
154 /* Retrieve the element from the node. */
155 return details::get_element_from_node<ContainedType, NodeType, Member>(
160 iterator(cds_lfht& ht, const cds_lfht_iter& it) : _ht(ht), _it(it)
164 /* Reference to the hash table being iterated over. */
166 /* Native lfht iterator pointing to the current position. */
170 explicit lfht_iteration_adapter(cds_lfht& ht) : _ht(ht)
174 /* Return an iterator to the beginning of the hash table. */
175 iterator begin() const noexcept
179 cds_lfht_first(&_ht, &it);
180 return iterator(_ht, it);
183 /* Return an iterator to the end of the hash table. */
184 iterator end() const noexcept
186 const cds_lfht_iter it = {};
188 return iterator(_ht, it);
192 /* Reference to the hash table being iterated over. */
194 /* RCU read lock held during the iteration. */
195 const lttng::urcu::read_lock_guard read_lock;
199 * The lfht_filtered_iteration_adapter class template wraps the liburcu lfht API to provide
200 * iteration capabilities over a result set. It allows users to iterate over a lock-free hash
201 * table's elements matching a given key with ranged-for semantics while holding the RCU read lock.
202 * The reader lock is held for the lifetime of the iteration adapter (i.e. not the lifetime of the
203 * iterators it provides).
205 template <typename ContainedType, typename NodeType, NodeType ContainedType::*Member, typename KeyType>
206 class lfht_filtered_iteration_adapter
207 : public lfht_iteration_adapter<ContainedType, NodeType, Member> {
209 /* Nested iterator class defines the iterator for lfht_filtered_iteration_adapter. */
210 class iterator : public lfht_iteration_adapter<ContainedType, NodeType, Member>::iterator {
211 /* Allow lfht_filtered_iteration_adapter to access private members of iterator. */
212 friend lfht_filtered_iteration_adapter;
215 iterator(const iterator& other) = default;
216 iterator(iterator&& other) noexcept = default;
217 ~iterator() = default;
218 iterator& operator=(const iterator&) = delete;
219 iterator& operator=(iterator&&) noexcept = delete;
221 /* Move to the next element in the result set. */
222 iterator& operator++()
224 LTTNG_ASSERT(this->_it.node);
225 /* NOLINTBEGIN(cppcoreguidelines-pro-type-const-cast) */
226 cds_lfht_next_duplicate(
229 reinterpret_cast<void *>(const_cast<KeyType *>(_key)),
231 /* NOLINTEND(cppcoreguidelines-pro-type-const-cast) */
236 iterator(cds_lfht& ht,
237 const cds_lfht_iter& it,
239 cds_lfht_match_fct match_function) :
240 lfht_iteration_adapter<ContainedType, NodeType, Member>::iterator(ht, it),
242 _match_function(match_function)
246 /* Only used to create an end iterator. */
247 iterator(cds_lfht& ht, const cds_lfht_iter& it) :
248 lfht_iteration_adapter<ContainedType, NodeType, Member>::iterator(ht, it),
250 _match_function(nullptr)
255 const cds_lfht_match_fct _match_function;
258 explicit lfht_filtered_iteration_adapter(cds_lfht& ht,
260 unsigned long key_hash,
261 cds_lfht_match_fct match_function) :
262 lfht_iteration_adapter<ContainedType, NodeType, Member>(ht),
265 _match_function(match_function)
269 /* Return an iterator to the first result. */
270 iterator begin() const noexcept
274 /* NOLINTBEGIN(cppcoreguidelines-pro-type-const-cast) */
275 cds_lfht_lookup(&this->_ht,
278 reinterpret_cast<void *>(const_cast<KeyType *>(_key)),
280 /* NOLINTEND(cppcoreguidelines-pro-type-const-cast) */
281 return iterator(this->_ht, it, _key, _match_function);
284 iterator end() const noexcept
286 const cds_lfht_iter it = {};
288 return iterator(this->_ht, it);
293 const unsigned long _key_hash;
294 const cds_lfht_match_fct _match_function;
297 template <typename ContainedType, cds_list_head ContainedType::*Member>
298 class list_iteration_adapter {
300 /* Nested iterator class defines the iterator for list_iteration_adapter. */
301 class iterator : public std::iterator<std::input_iterator_tag, std::uint64_t> {
302 /* Allow list_iteration_adapter to access private members of iterator. */
303 friend list_iteration_adapter;
306 iterator(const iterator& other) = default;
307 iterator(iterator&& other) noexcept = default;
308 ~iterator() = default;
309 iterator& operator=(const iterator&) = delete;
310 iterator& operator=(iterator&&) noexcept = delete;
312 /* Move to the next element in the hash table. */
313 iterator& operator++()
315 _node = _node_contents.next;
316 _node_contents = *_node;
320 bool operator==(const iterator& other) const noexcept
322 return other._node == _node;
325 bool operator!=(const iterator& other) const noexcept
327 return !(*this == other);
330 /* Dereference the iterator to access the contained element. */
331 ContainedType *operator*() const
333 /* Retrieve the element from the node. */
334 return lttng::utils::container_of(_node, Member);
338 explicit iterator(const cds_list_head& node) : _node(&node), _node_contents(node)
343 const cds_list_head *_node;
344 /* Copy of node contents to allow safe deletion during the iteration. */
345 cds_list_head _node_contents;
348 explicit list_iteration_adapter(const cds_list_head& list) : _list(list)
352 /* Return an iterator to the beginning of the hash table. */
353 iterator begin() const noexcept
355 return iterator(*_list.next);
358 /* Return an iterator to the end of the hash table. */
359 iterator end() const noexcept
361 return iterator(_list);
365 /* Reference to the list being iterated over. */
366 const cds_list_head& _list;
369 template <typename ContainedType, cds_list_head ContainedType::*Member>
370 class rcu_list_iteration_adapter : public list_iteration_adapter<ContainedType, Member> {
372 /* Nested iterator class defines the iterator for rcu_list_iteration_adapter. */
373 class iterator : public list_iteration_adapter<ContainedType, Member>::iterator {
374 /* Allow rcu_list_iteration_adapter to access private members of iterator. */
375 friend rcu_list_iteration_adapter;
378 iterator(const iterator& other) = default;
379 iterator(iterator&& other) noexcept = default;
380 ~iterator() = default;
381 iterator& operator=(const iterator&) = delete;
382 iterator& operator=(iterator&&) noexcept = delete;
384 /* Move to the next element in the hash table. */
385 iterator& operator++()
387 this->_node = rcu_dereference(this->_node_contents.next);
388 this->_node_contents = *this->_node;
393 explicit iterator(const cds_list_head& node) :
394 list_iteration_adapter<ContainedType, Member>::iterator(node)
399 explicit rcu_list_iteration_adapter(const cds_list_head& list) :
400 list_iteration_adapter<ContainedType, Member>(list)
404 /* Return an iterator to the beginning of the hash table. */
405 iterator begin() const noexcept
407 return iterator(*rcu_dereference(this->_list.next));
410 /* Return an iterator to the end of the hash table. */
411 iterator end() const noexcept
413 return iterator(this->_list);
417 /* RCU read lock held during the iteration. */
418 const lttng::urcu::read_lock_guard read_lock;
421 } /* namespace urcu */
422 } /* namespace lttng */
424 #endif /* LTTNG_URCU_H */