aboutsummaryrefslogtreecommitdiff
path: root/layers/vk_layer_utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'layers/vk_layer_utils.h')
-rw-r--r--layers/vk_layer_utils.h158
1 files changed, 158 insertions, 0 deletions
diff --git a/layers/vk_layer_utils.h b/layers/vk_layer_utils.h
index 723bc004a..b08d30de2 100644
--- a/layers/vk_layer_utils.h
+++ b/layers/vk_layer_utils.h
@@ -159,3 +159,161 @@ static inline int u_ffs(int val) {
#ifdef __cplusplus
}
#endif
+
+// shared_mutex support added in MSVC 2015 update 2
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && NTDDI_VERSION > NTDDI_WIN10_RS2
+#include <shared_mutex>
+#endif
+
+// Limited concurrent_unordered_map that supports internally-synchronized
+// insert/erase/access. Splits locking across N buckets and uses shared_mutex
+// for read/write locking. Iterators are not supported. The following
+// operations are supported:
+//
+// insert_or_assign: Insert a new element or update an existing element.
+// insert: Insert a new element and return whether it was inserted.
+// erase: Remove an element.
+// contains: Returns true if the key is in the map.
+// find: Returns != end() if found, value is in ret->second.
+// pop: Erases and returns the erased value if found.
+//
+// find/end: find returns a vaguely iterator-like type that can be compared to
+// end and can use iter->second to retrieve the reference. This is to ease porting
+// for existing code that combines the existence check and lookup in a single
+// operation (and thus a single lock). i.e.:
+//
+// auto iter = map.find(key);
+// if (iter != map.end()) {
+// T t = iter->second;
+// ...
+//
+// snapshot: Return an array of elements (key, value pairs) that satisfy an optional
+// predicate. This can be used as a substitute for iterators in exceptional cases.
+template <typename Key, typename T, int BUCKETSLOG2 = 2>
+class vl_concurrent_unordered_map {
+ public:
+ void insert_or_assign(const Key &key, const T &value) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ write_lock_guard_t lock(locks[h].lock);
+ maps[h][key] = value;
+ }
+
+ bool insert(const Key &key, const T &value) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ write_lock_guard_t lock(locks[h].lock);
+ auto ret = maps[h].insert(typename std::unordered_map<Key, T>::value_type(key, value));
+ return ret.second;
+ }
+
+ // returns size_type
+ size_t erase(const Key &key) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ write_lock_guard_t lock(locks[h].lock);
+ return maps[h].erase(key);
+ }
+
+ bool contains(const Key &key) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ read_lock_guard_t lock(locks[h].lock);
+ return maps[h].count(key) != 0;
+ }
+
+ // type returned by find() and end().
+ class FindResult {
+ public:
+ FindResult(bool a, T b) : result(a, std::move(b)) {}
+
+ // == and != only support comparing against end()
+ bool operator==(const FindResult &other) const {
+ if (result.first == false && other.result.first == false) {
+ return true;
+ }
+ return false;
+ }
+ bool operator!=(const FindResult &other) const { return !(*this == other); }
+
+ // Make -> act kind of like an iterator.
+ std::pair<bool, T> *operator->() { return &result; }
+ const std::pair<bool, T> *operator->() const { return &result; }
+
+ private:
+ // (found, reference to element)
+ std::pair<bool, T> result;
+ };
+
+ // find()/end() return a FindResult containing a copy of the value. For end(),
+ // return a default value.
+ FindResult end() { return FindResult(false, T()); }
+
+ FindResult find(const Key &key) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ read_lock_guard_t lock(locks[h].lock);
+
+ auto itr = maps[h].find(key);
+ bool found = itr != maps[h].end();
+
+ if (found) {
+ return FindResult(true, itr->second);
+ } else {
+ return end();
+ }
+ }
+
+ FindResult pop(const Key &key) {
+ uint32_t h = ConcurrentMapHashObject(key);
+ write_lock_guard_t lock(locks[h].lock);
+
+ auto itr = maps[h].find(key);
+ bool found = itr != maps[h].end();
+
+ if (found) {
+ auto ret = std::move(FindResult(true, itr->second));
+ maps[h].erase(itr);
+ return ret;
+ } else {
+ return end();
+ }
+ }
+
+ std::vector<std::pair<const Key, T>> snapshot(std::function<bool(T)> f = nullptr) {
+ std::vector<std::pair<const Key, T>> ret;
+ for (int h = 0; h < BUCKETS; ++h) {
+ read_lock_guard_t lock(locks[h].lock);
+ for (auto j : maps[h]) {
+ if (!f || f(j.second)) {
+ ret.push_back(j);
+ }
+ }
+ }
+ return ret;
+ }
+
+ private:
+ static const int BUCKETS = (1 << BUCKETSLOG2);
+// shared_mutex support added in MSVC 2015 update 2
+#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && NTDDI_VERSION > NTDDI_WIN10_RS2
+#include <shared_mutex>
+ typedef std::shared_mutex lock_t;
+ typedef std::shared_lock<lock_t> read_lock_guard_t;
+ typedef std::unique_lock<lock_t> write_lock_guard_t;
+#else
+ typedef std::mutex lock_t;
+ typedef std::unique_lock<lock_t> read_lock_guard_t;
+ typedef std::unique_lock<lock_t> write_lock_guard_t;
+#endif
+
+ std::unordered_map<Key, T> maps[BUCKETS];
+ struct {
+ lock_t lock;
+ // Put each lock on its own cache line to avoid false cache line sharing.
+ char padding[(-int(sizeof(lock_t))) & 63];
+ } locks[BUCKETS];
+
+ uint32_t ConcurrentMapHashObject(const Key &object) const {
+ uint64_t u64 = (uint64_t)(uintptr_t)object;
+ uint32_t hash = (uint32_t)(u64 >> 32) + (uint32_t)u64;
+ hash ^= (hash >> BUCKETSLOG2) ^ (hash >> (2 * BUCKETSLOG2));
+ hash &= (BUCKETS - 1);
+ return hash;
+ }
+};