--- /dev/null
+// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. This
+// source code is licensed under the BSD-style license found in the LICENSE
+// file in the root directory of this source tree. An additional grant of
+// patent rights can be found in the PATENTS file in the same directory.
+//
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+#include <utility>
+
+#include "util/autovector.h"
+
+namespace rocksdb {
+
+// This is similar to std::unordered_map, except that it tries to avoid
+// allocating or deallocating memory as much as possible. With
+// std::unordered_map, an allocation/deallocation is made for every insertion
+// or deletion because of the requirement that iterators remain valid even
+// with insertions or deletions. This means that the hash chains will be
+// implemented as linked lists.
+//
+// This implementation uses autovector as hash chains insteads.
+//
+template <typename K, typename V, size_t size = 128>
+class HashMap {
+ std::array<autovector<std::pair<K, V>, 1>, size> table_;
+
+ public:
+ bool Contains(K key) {
+ auto& bucket = table_[key % size];
+ auto it = std::find_if(
+ bucket.begin(), bucket.end(),
+ [key](const std::pair<K, V>& p) { return p.first == key; });
+ return it != bucket.end();
+ }
+
+ void Insert(K key, V value) {
+ auto& bucket = table_[key % size];
+ bucket.push_back({key, value});
+ }
+
+ void Delete(K key) {
+ auto& bucket = table_[key % size];
+ auto it = std::find_if(
+ bucket.begin(), bucket.end(),
+ [key](const std::pair<K, V>& p) { return p.first == key; });
+ if (it != bucket.end()) {
+ auto last = bucket.end() - 1;
+ if (it != last) {
+ *it = *last;
+ }
+ bucket.pop_back();
+ }
+ }
+
+ V& Get(K key) {
+ auto& bucket = table_[key % size];
+ auto it = std::find_if(
+ bucket.begin(), bucket.end(),
+ [key](const std::pair<K, V>& p) { return p.first == key; });
+ return it->second;
+ }
+};
+
+} // namespace rocksdb
void TransactionLockMgr::DecrementWaitersImpl(const TransactionImpl* txn,
TransactionID wait_id) {
auto id = txn->GetID();
- assert(wait_txn_map_.count(id) > 0);
- wait_txn_map_.erase(id);
+ assert(wait_txn_map_.Contains(id));
+ wait_txn_map_.Delete(id);
- rev_wait_txn_map_[wait_id]--;
- if (rev_wait_txn_map_[wait_id] == 0) {
- rev_wait_txn_map_.erase(wait_id);
+ rev_wait_txn_map_.Get(wait_id)--;
+ if (rev_wait_txn_map_.Get(wait_id) == 0) {
+ rev_wait_txn_map_.Delete(wait_id);
}
}
TransactionID wait_id) {
auto id = txn->GetID();
std::lock_guard<std::mutex> lock(wait_txn_map_mutex_);
- assert(wait_txn_map_.count(id) == 0);
- wait_txn_map_[id] = wait_id;
- rev_wait_txn_map_[wait_id]++;
+ assert(!wait_txn_map_.Contains(id));
+ wait_txn_map_.Insert(id, wait_id);
+
+ if (rev_wait_txn_map_.Contains(wait_id)) {
+ rev_wait_txn_map_.Get(wait_id)++;
+ } else {
+ rev_wait_txn_map_.Insert(wait_id, 1);
+ }
// No deadlock if nobody is waiting on self.
- if (rev_wait_txn_map_.count(id) == 0) {
+ if (!rev_wait_txn_map_.Contains(id)) {
return false;
}
if (next == id) {
DecrementWaitersImpl(txn, wait_id);
return true;
- } else if (wait_txn_map_.count(next) == 0) {
+ } else if (!wait_txn_map_.Contains(next)) {
return false;
} else {
- next = wait_txn_map_[next];
+ next = wait_txn_map_.Get(next);
}
}
#include <vector>
#include "rocksdb/utilities/transaction.h"
+#include "util/hash_map.h"
#include "util/instrumented_mutex.h"
#include "util/thread_local.h"
#include "utilities/transactions/transaction_impl.h"
std::mutex wait_txn_map_mutex_;
// Maps from waitee -> number of waiters.
- std::unordered_map<TransactionID, int> rev_wait_txn_map_;
+ HashMap<TransactionID, int> rev_wait_txn_map_;
// Maps from waiter -> waitee.
- std::unordered_map<TransactionID, TransactionID> wait_txn_map_;
+ HashMap<TransactionID, TransactionID> wait_txn_map_;
// Used to allocate mutexes/condvars to use when locking keys
std::shared_ptr<TransactionDBMutexFactory> mutex_factory_;