|
|
@ -7,7 +7,10 @@ |
|
|
|
#include <memory>
|
|
|
|
#include <cassert>
|
|
|
|
#include <optional>
|
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
|
|
|
|
using namespace std::chrono_literals; |
|
|
|
/**
|
|
|
|
Pensé en un mundo sin memoria, sin tiempo; consideré la posibilidad de un lenguaje |
|
|
|
que ignorara los sustantivos, un lenguaje de verbos impersonales y de indeclinables |
|
|
@ -72,12 +75,88 @@ namespace mct20 { |
|
|
|
|
|
|
|
template<typename K, typename V> |
|
|
|
class bucket { |
|
|
|
constexpr static uint32_t delete_mode = 65536; |
|
|
|
std::atomic<uint32_t> delete_lock; |
|
|
|
|
|
|
|
void reader_lock() { |
|
|
|
while(delete_lock.fetch_add(1) >= delete_mode) { |
|
|
|
delete_lock.fetch_sub(1); |
|
|
|
std::this_thread::yield(); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
void writer_lock() { |
|
|
|
while(delete_lock.fetch_add(delete_mode) >= delete_mode) { |
|
|
|
delete_lock.fetch_sub(delete_mode); |
|
|
|
std::this_thread::yield(); |
|
|
|
} |
|
|
|
while(delete_lock.load() != delete_mode) { |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
void reader_unlock() { |
|
|
|
delete_lock.fetch_sub(1); |
|
|
|
} |
|
|
|
|
|
|
|
void writer_unlock() { |
|
|
|
delete_lock.fetch_sub(delete_mode); |
|
|
|
} |
|
|
|
|
|
|
|
struct RGuard { |
|
|
|
bucket& master; |
|
|
|
RGuard(bucket& m) |
|
|
|
: master(m) |
|
|
|
{master.reader_lock();} |
|
|
|
|
|
|
|
~RGuard() |
|
|
|
{master.reader_unlock();} |
|
|
|
}; |
|
|
|
|
|
|
|
struct WGuard { |
|
|
|
bucket& master; |
|
|
|
WGuard(bucket& m) |
|
|
|
: master(m) |
|
|
|
{master.writer_lock();} |
|
|
|
|
|
|
|
~WGuard() |
|
|
|
{master.writer_unlock();} |
|
|
|
}; |
|
|
|
public: |
|
|
|
bucket() |
|
|
|
: start{nullptr} |
|
|
|
{} |
|
|
|
|
|
|
|
void remove(size_t hash, const K& key) { |
|
|
|
WGuard _g(*this); |
|
|
|
auto it = start.load(); |
|
|
|
auto prev = &start; |
|
|
|
do{ |
|
|
|
if(it == nullptr) return; |
|
|
|
while(it->contents.hash != hash) |
|
|
|
{ |
|
|
|
prev = reinterpret_cast<node_ptr*>(&(it->contents.next)); |
|
|
|
it = (node*)it->contents.next.load(); |
|
|
|
if(it == nullptr) return; |
|
|
|
} |
|
|
|
if(it->contents.key == key) { |
|
|
|
prev->store(reinterpret_cast<node*>(it->contents.next.load())); |
|
|
|
it->contents.references.fetch_sub(1); |
|
|
|
while(it->contents.references.load()!=0) { |
|
|
|
std::this_thread::yield(); |
|
|
|
} |
|
|
|
delete it->contents.ptr; |
|
|
|
delete it; |
|
|
|
return; |
|
|
|
} |
|
|
|
prev = reinterpret_cast<node_ptr*>(&(it->contents.next)); |
|
|
|
it = (node*)it->contents.next.load(); |
|
|
|
} while(true); |
|
|
|
} |
|
|
|
|
|
|
|
void push(size_t hash, const K& key, const V& value) { |
|
|
|
RGuard _g(*this); |
|
|
|
auto t = new node{ |
|
|
|
.contents = node_contents{ |
|
|
|
.key{key}, |
|
|
@ -101,6 +180,7 @@ namespace mct20 { |
|
|
|
} |
|
|
|
|
|
|
|
std::optional<accessor<V>> get(const size_t hash, const K& key) { |
|
|
|
RGuard _g(*this); |
|
|
|
auto v = start.load(); |
|
|
|
while(v) { |
|
|
|
if(v->contents.references.fetch_add(1)!=0) |
|
|
@ -186,6 +266,16 @@ public: |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
void remove(const K& key) { |
|
|
|
const auto l = hash{}(key); |
|
|
|
auto& ref = buckets[l%bucket_count]; |
|
|
|
ref.remove(l, key); |
|
|
|
const auto l2 = _details_::rotl(l, sizeof(size_t)*4); |
|
|
|
auto& ref2 = buckets[l2%bucket_count]; |
|
|
|
ref2.remove(l2, key); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
lfhmap() { |
|
|
|
for(auto& a : buckets) { |
|
|
|
a.start = nullptr; |
|
|
|