// thread_safe_lookup_table.h
#ifndef THREAD_SAFE_LOOKUP_TABLE_H
#define THREAD_SAFE_LOOKUP_TABLE_H
#include <atomic>
#include <vector>
#include <string>
#include <sstream>
#include <list>
#include <algorithm>
#include <memory>
#include <mutex>
#include <iostream>
static const unsigned kDefaultBucketNumber = 19;
template<typename KeyType,typename ValueType, typename HashType=std::hash<KeyType>>
class ThreadSafeLookupTable {
public:
static const int kStatusInit = 0x1;
static const int kStatusSwapping = 0x2;
static const int kStatusRestoreAfterSwapped = 0x4;
static const int kStatusDeleteAfterSwapped = 0x8;
static const int kStatusAll = 0x8000-1;
static const int kIndexKey = 0;
static const int kIndexValue = 1;
static const int kIndexStatus = 2;
using KeyTypeP = std::shared_ptr<KeyType>;
using ValueTypeP = std::shared_ptr<ValueType>;
using AtomicIntP = std::shared_ptr<std::atomic<int>>;
using BucketValueType = std::tuple<KeyTypeP,ValueTypeP,AtomicIntP>;
using BucketValueTypeP = std::shared_ptr<BucketValueType>;
private:
class BucketType {
private:
using bucketDataType = std::list<BucketValueTypeP>;
using bucketIterator = typename bucketDataType::iterator;
bucketDataType data_;
mutable std::mutex mutex_;
bucketIterator findIter(KeyTypeP& keyP, int status=kStatusAll) {
return std::find_if(data_.begin(), data_.end(),
[&](BucketValueTypeP& one){
KeyTypeP& kP = std::get<kIndexKey>(*one);
return (kP==keyP || *kP==*keyP) && ((*(std::get<kIndexStatus>(*one)) & status) != 0);
});
}
public:
BucketValueTypeP findItem(KeyTypeP& keyP, int status) {
std::lock_guard<std::mutex> lock(mutex_);
bucketIterator iter = findIter(keyP, status);
return (iter == data_.end())? BucketValueTypeP(): *iter;
}
bool add(KeyTypeP& keyP, ValueTypeP& valueP, int status) {
std::lock_guard<std::mutex> lock(mutex_);
bucketIterator iter = findIter(keyP);
if (iter != data_.end()) {
//std::cout << "already exist!" << std::endl;
return false;
}
auto status_p = std::make_shared<std::atomic<int>>(status);
BucketValueType one_tuple = std::make_tuple(std::move(keyP), std::move(valueP), std::move(status_p));
auto one_tuple_p = std::make_shared<BucketValueType>(std::move(one_tuple));
data_.push_back(one_tuple_p);
return true;
}
void del(KeyTypeP& keyP) {
std::lock_guard<std::mutex> lock(mutex_);
bucketIterator iter = findIter(keyP);
if (iter != data_.end()) {
data_.erase(iter);
}
}
BucketValueTypeP findItemAndChangeStatus(KeyTypeP& keyP, int in_status, int out_status) {
std::lock_guard<std::mutex> lock(mutex_);
bucketIterator iter = findIter(keyP, in_status);
if (iter == data_.end()) {
BucketValueTypeP();
} else {
auto& sp = std::get<kIndexStatus>(**iter);
*sp = out_status;
return *iter;
}
}
std::string debug_info() {
std::lock_guard<std::mutex> lock(mutex_);
std::ostringstream oss;
oss << "->";
for (auto it = data_.begin(); it != data_.end(); ++it) {
BucketValueType& one = **it;
oss << " [" << *std::get<kIndexKey>(one)
<< "," << *std::get<kIndexValue>(one)
<< "," << *std::get<kIndexStatus>(one)
<< "] ->";
}
oss << " [tail]";
return oss.str();
}
};
using BucketTypeP = std::shared_ptr<BucketType>;
std::vector<BucketTypeP> buckets_;
HashType hasher_;
BucketTypeP getBucket(KeyTypeP& keyP) {
std::size_t const bucket_index = hasher_(*keyP) % buckets_.size();
return buckets_[bucket_index];
}
public:
ThreadSafeLookupTable(const unsigned bucket_num=kDefaultBucketNumber, const HashType& hasher=HashType()):
buckets_(bucket_num), hasher_(hasher) {
for(unsigned i = 0; i < bucket_num; ++i) {
buckets_[i].reset(new BucketType);
}
}
ThreadSafeLookupTable(const ThreadSafeLookupTable& other) = delete;
ThreadSafeLookupTable& operator=(const ThreadSafeLookupTable& other) = delete;
bool add(KeyTypeP& keyP, ValueTypeP& valueP, int status=kStatusInit) {
return getBucket(keyP)->add(keyP, valueP, status);
}
bool add(KeyType key, ValueType value, int status=kStatusInit) {
auto kp = std::make_shared<KeyType>(std::move(key));
auto vp = std::make_shared<ValueType>(std::move(value));
return add(kp, vp, status);
}
void del(KeyTypeP keyP) {
return getBucket(keyP)->del(keyP);
}
void del(KeyType key) {
del(std::make_shared<KeyType>(std::move(key)));
}
BucketValueTypeP findItem(KeyTypeP keyP, int status=kStatusAll) {
return getBucket(keyP)->findItem(keyP, status);
}
BucketValueTypeP findItem(KeyType key, int status=kStatusAll) {
return findItem(std::make_shared<KeyType>(std::move(key)), status);
}
BucketValueTypeP findItemAndChangeStatus(KeyTypeP keyP, int in_status, int out_status) {
return getBucket(keyP)->findItemAndChangeStatus(keyP, in_status, out_status);
}
BucketValueTypeP findItemAndChangeStatus(KeyType key, int in_status, int out_status) {
return findItemAndChangeStatus(std::make_shared<KeyType>(std::move(key)), in_status, out_status);
}
std::string debug_info() {
std::ostringstream oss;
for (int i = 0; i < buckets_.size(); ++i) {
oss << "[" << i << "] " << buckets_[i]->debug_info();
if (i < buckets_.size() - 1) {
oss << std::endl;
}
}
return oss.str();
}
};
#endif /* THREAD_SAFE_LOOKUP_TABLE_H */
// test.cpp
#include "thread_safe_lookup_table.h"
#include <iostream>
using namespace std;
int main() {
using KeyType = string;
using ValueType = string;
using KeyTypeP = std::shared_ptr<KeyType>;
using ValueTypeP = std::shared_ptr<ValueType>;
using AtomicIntP = std::shared_ptr<std::atomic<int>>;
using BucketValueType = std::tuple<KeyTypeP,ValueTypeP,AtomicIntP>;
using BucketValueTypeP = std::shared_ptr<BucketValueType>;
ThreadSafeLookupTable<KeyType, ValueType> myTable;
myTable.add("000", "bba");
myTable.add("001", "bbb");
myTable.add("002", "bbc");
myTable.add("003", "bbc");
myTable.add("004", "bbc");
myTable.add("005", "bbc");
std::cout << myTable.debug_info() << std::endl;
myTable.del("003");
cout << "del 003 -----------------" << endl;
std::cout << myTable.debug_info() << std::endl;
cout << "add 005 -----------------" << endl;
myTable.add("005", "bb5");
std::cout << myTable.debug_info() << std::endl;
cout << "change 005 -----------------" << endl;
BucketValueTypeP item = myTable.findItem("005");
auto& kp = std::get<0>(*item);
auto& vp = std::get<1>(*item);
auto& sp = std::get<2>(*item);
vp = make_shared<KeyType>("hehe");
*sp = 33;
std::cout << myTable.debug_info() << std::endl;
cout << "findItemAndChangeStatus 002 -----------------" << endl;
item = myTable.findItemAndChangeStatus("002", 1, 999);
std::cout << myTable.debug_info() << std::endl;
return 0;
}
$ g++ test.cpp -o test && ./test
[0] -> [tail]
[1] -> [003,bbc,1] -> [tail]
[2] -> [004,bbc,1] -> [tail]
[3] -> [tail]
[4] -> [tail]
[5] -> [tail]
[6] -> [001,bbb,1] -> [002,bbc,1] -> [tail]
[7] -> [tail]
[8] -> [tail]
[9] -> [tail]
[10] -> [tail]
[11] -> [tail]
[12] -> [tail]
[13] -> [tail]
[14] -> [005,bbc,1] -> [tail]
[15] -> [tail]
[16] -> [tail]
[17] -> [000,bba,1] -> [tail]
[18] -> [tail]
del 003 -----------------
[0] -> [tail]
。。。