7#ifndef EXT_RMANPKG_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_CONCURRENT_MAP_H
8#define EXT_RMANPKG_PLUGIN_RENDERMAN_PLUGIN_HD_PRMAN_CONCURRENT_MAP_H
12#include <tbb/concurrent_unordered_map.h>
13#include <tbb/spin_rw_mutex.h>
22PXR_NAMESPACE_OPEN_SCOPE
42 typename Hash = std::hash<Key>,
43 typename KeyEqual = std::equal_to<Key>>
44class HdPrmanConcurrentMap
48 struct _is_atomic : std::false_type { };
51 struct _is_atomic<
std::atomic<U>> : std::true_type { };
56 struct _atomic_value_type;
59 struct _atomic_value_type<
std::atomic<U>> {
using type = U; };
62 using _atomic_value_t =
typename _atomic_value_type<U>::type;
65 _is_atomic<T>::value ||
66 std::is_move_constructible_v<T> ||
67 std::is_default_constructible_v<T>,
68 "HdPrmanConcurrentMap<Key, T>: T must be move- or default- "
69 "constructible or std::atomic<U> for integral U.");
72 T& get(
const Key& key)
74 if constexpr (_is_atomic<T>::value) {
75 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
76 return _get_atomic(key, lock);
78 if constexpr (std::is_move_constructible_v<T>) {
79 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
82 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
83 auto it = _map.find(key);
84 if (it == _map.end()) {
85 if (!lock.upgrade_to_writer()) {
88 if (it == _map.end()) {
90 std::piecewise_construct,
91 std::forward_as_tuple(key),
92 std::tuple<>{}).first;
102 bool withIfPresent(
const Key& key, std::function<
void(T&)> fn)
104 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
105 auto it = _map.find(key);
106 if (it == _map.end()) {
return false; }
114 void iterate(std::function<
void(
const Key&, T&)> fn)
117 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
true);
118 for (
auto& p : _map) {
119 fn(p.first, p.second);
126 void citerate(std::function<
void(
const Key&,
const T&)> fn)
const
128 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
129 for (
const auto& p : _map) {
130 fn(p.first, p.second);
137 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
142 void erase(
const Key& key)
145 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
true);
146 _map.unsafe_erase(key);
153 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
true);
160 typename = std::enable_if_t<
161 !_is_atomic<U>::value &&
162 std::is_copy_assignable_v<U>>>
163 void set(
const Key& key,
const T& val)
165 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
166 if constexpr (std::is_move_constructible_v<T>) {
170 auto it = _map.find(key);
171 if (it == _map.end()) {
172 if (!lock.upgrade_to_writer()) {
175 if (it == _map.end()) {
176 if constexpr (std::is_copy_constructible_v<T>) {
177 _map.insert({ key, val });
181 std::piecewise_construct,
182 std::forward_as_tuple(key),
183 std::tuple<>{}).first;
192 typename = std::enable_if_t<_is_atomic<U>::value>>
193 _atomic_value_t<U> load(
195 std::memory_order order = std::memory_order_seq_cst)
197 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
198 return _get_atomic(key, lock).load(order);
205 typename = std::enable_if_t<
206 _is_atomic<U>::value &&
207 std::is_integral_v<_atomic_value_t<U>>>>
208 _atomic_value_t<U> inc(
const Key& key)
210 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
211 return ++_get_atomic(key, lock);
218 typename = std::enable_if_t<
219 _is_atomic<U>::value &&
220 std::is_integral_v<_atomic_value_t<U>>>>
221 _atomic_value_t<U> dec(
const Key& key)
223 tbb::spin_rw_mutex::scoped_lock lock(_mutex,
false);
224 return --_get_atomic(key, lock);
228 template<typename U = T, typename = std::enable_if_t<_is_atomic<U>::value>>
229 T& _get_atomic(
const Key& key, tbb::spin_rw_mutex::scoped_lock& lock)
231 auto it = _map.find(key);
232 if (it == _map.end()) {
233 if (!lock.upgrade_to_writer()) {
236 if (it == _map.end()) {
245 std::piecewise_construct,
246 std::forward_as_tuple(key),
247 std::forward_as_tuple(_atomic_value_t<T>{})).first;
253 using _MapType = tbb::concurrent_unordered_map<Key, T, Hash, KeyEqual>;
256 mutable tbb::spin_rw_mutex _mutex;
259PXR_NAMESPACE_CLOSE_SCOPE