13cab2bb3Spatrick //===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // Concurrent uptr->T hashmap.
103cab2bb3Spatrick //
113cab2bb3Spatrick //===----------------------------------------------------------------------===//
123cab2bb3Spatrick
133cab2bb3Spatrick #ifndef SANITIZER_ADDRHASHMAP_H
143cab2bb3Spatrick #define SANITIZER_ADDRHASHMAP_H
153cab2bb3Spatrick
163cab2bb3Spatrick #include "sanitizer_common.h"
173cab2bb3Spatrick #include "sanitizer_mutex.h"
183cab2bb3Spatrick #include "sanitizer_atomic.h"
193cab2bb3Spatrick #include "sanitizer_allocator_internal.h"
203cab2bb3Spatrick
213cab2bb3Spatrick namespace __sanitizer {
223cab2bb3Spatrick
233cab2bb3Spatrick // Concurrent uptr->T hashmap.
243cab2bb3Spatrick // T must be a POD type, kSize is preferably a prime but can be any number.
253cab2bb3Spatrick // Usage example:
263cab2bb3Spatrick //
273cab2bb3Spatrick // typedef AddrHashMap<uptr, 11> Map;
283cab2bb3Spatrick // Map m;
293cab2bb3Spatrick // {
303cab2bb3Spatrick // Map::Handle h(&m, addr);
313cab2bb3Spatrick // use h.operator->() to access the data
323cab2bb3Spatrick // if h.created() then the element was just created, and the current thread
333cab2bb3Spatrick // has exclusive access to it
343cab2bb3Spatrick // otherwise the current thread has only read access to the data
353cab2bb3Spatrick // }
363cab2bb3Spatrick // {
373cab2bb3Spatrick // Map::Handle h(&m, addr, true);
383cab2bb3Spatrick // this will remove the data from the map in Handle dtor
393cab2bb3Spatrick // the current thread has exclusive access to the data
403cab2bb3Spatrick // if !h.exists() then the element never existed
413cab2bb3Spatrick // }
42*810390e3Srobert // {
43*810390e3Srobert // Map::Handle h(&m, addr, false, true);
44*810390e3Srobert // this will create a new element or return a handle to an existing element
45*810390e3Srobert // if !h.created() this thread does *not* have exclusive access to the data
46*810390e3Srobert // }
473cab2bb3Spatrick template<typename T, uptr kSize>
483cab2bb3Spatrick class AddrHashMap {
493cab2bb3Spatrick private:
503cab2bb3Spatrick struct Cell {
513cab2bb3Spatrick atomic_uintptr_t addr;
523cab2bb3Spatrick T val;
533cab2bb3Spatrick };
543cab2bb3Spatrick
553cab2bb3Spatrick struct AddBucket {
563cab2bb3Spatrick uptr cap;
573cab2bb3Spatrick uptr size;
583cab2bb3Spatrick Cell cells[1]; // variable len
593cab2bb3Spatrick };
603cab2bb3Spatrick
613cab2bb3Spatrick static const uptr kBucketSize = 3;
623cab2bb3Spatrick
633cab2bb3Spatrick struct Bucket {
64*810390e3Srobert Mutex mtx;
653cab2bb3Spatrick atomic_uintptr_t add;
663cab2bb3Spatrick Cell cells[kBucketSize];
673cab2bb3Spatrick };
683cab2bb3Spatrick
693cab2bb3Spatrick public:
703cab2bb3Spatrick AddrHashMap();
713cab2bb3Spatrick
723cab2bb3Spatrick class Handle {
733cab2bb3Spatrick public:
743cab2bb3Spatrick Handle(AddrHashMap<T, kSize> *map, uptr addr);
753cab2bb3Spatrick Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove);
763cab2bb3Spatrick Handle(AddrHashMap<T, kSize> *map, uptr addr, bool remove, bool create);
773cab2bb3Spatrick
783cab2bb3Spatrick ~Handle();
793cab2bb3Spatrick T *operator->();
803cab2bb3Spatrick T &operator*();
813cab2bb3Spatrick const T &operator*() const;
823cab2bb3Spatrick bool created() const;
833cab2bb3Spatrick bool exists() const;
843cab2bb3Spatrick
853cab2bb3Spatrick private:
863cab2bb3Spatrick friend AddrHashMap<T, kSize>;
873cab2bb3Spatrick AddrHashMap<T, kSize> *map_;
883cab2bb3Spatrick Bucket *bucket_;
893cab2bb3Spatrick Cell *cell_;
903cab2bb3Spatrick uptr addr_;
913cab2bb3Spatrick uptr addidx_;
923cab2bb3Spatrick bool created_;
933cab2bb3Spatrick bool remove_;
943cab2bb3Spatrick bool create_;
953cab2bb3Spatrick };
963cab2bb3Spatrick
97*810390e3Srobert typedef void (*ForEachCallback)(const uptr key, const T &val, void *arg);
98*810390e3Srobert // ForEach acquires a lock on each bucket while iterating over
99*810390e3Srobert // elements. Note that this only ensures that the structure of the hashmap is
100*810390e3Srobert // unchanged, there may be a data race to the element itself.
101*810390e3Srobert void ForEach(ForEachCallback cb, void *arg);
102*810390e3Srobert
1033cab2bb3Spatrick private:
1043cab2bb3Spatrick friend class Handle;
1053cab2bb3Spatrick Bucket *table_;
1063cab2bb3Spatrick
1073cab2bb3Spatrick void acquire(Handle *h);
1083cab2bb3Spatrick void release(Handle *h);
1093cab2bb3Spatrick uptr calcHash(uptr addr);
1103cab2bb3Spatrick };
1113cab2bb3Spatrick
1123cab2bb3Spatrick template <typename T, uptr kSize>
ForEach(ForEachCallback cb,void * arg)113*810390e3Srobert void AddrHashMap<T, kSize>::ForEach(ForEachCallback cb, void *arg) {
114*810390e3Srobert for (uptr n = 0; n < kSize; n++) {
115*810390e3Srobert Bucket *bucket = &table_[n];
116*810390e3Srobert
117*810390e3Srobert ReadLock lock(&bucket->mtx);
118*810390e3Srobert
119*810390e3Srobert for (uptr i = 0; i < kBucketSize; i++) {
120*810390e3Srobert Cell *c = &bucket->cells[i];
121*810390e3Srobert uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
122*810390e3Srobert if (addr1 != 0)
123*810390e3Srobert cb(addr1, c->val, arg);
124*810390e3Srobert }
125*810390e3Srobert
126*810390e3Srobert // Iterate over any additional cells.
127*810390e3Srobert if (AddBucket *add =
128*810390e3Srobert (AddBucket *)atomic_load(&bucket->add, memory_order_acquire)) {
129*810390e3Srobert for (uptr i = 0; i < add->size; i++) {
130*810390e3Srobert Cell *c = &add->cells[i];
131*810390e3Srobert uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
132*810390e3Srobert if (addr1 != 0)
133*810390e3Srobert cb(addr1, c->val, arg);
134*810390e3Srobert }
135*810390e3Srobert }
136*810390e3Srobert }
137*810390e3Srobert }
138*810390e3Srobert
139*810390e3Srobert template<typename T, uptr kSize>
Handle(AddrHashMap<T,kSize> * map,uptr addr)1403cab2bb3Spatrick AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr) {
1413cab2bb3Spatrick map_ = map;
1423cab2bb3Spatrick addr_ = addr;
1433cab2bb3Spatrick remove_ = false;
1443cab2bb3Spatrick create_ = true;
1453cab2bb3Spatrick map_->acquire(this);
1463cab2bb3Spatrick }
1473cab2bb3Spatrick
1483cab2bb3Spatrick template<typename T, uptr kSize>
Handle(AddrHashMap<T,kSize> * map,uptr addr,bool remove)1493cab2bb3Spatrick AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
1503cab2bb3Spatrick bool remove) {
1513cab2bb3Spatrick map_ = map;
1523cab2bb3Spatrick addr_ = addr;
1533cab2bb3Spatrick remove_ = remove;
1543cab2bb3Spatrick create_ = true;
1553cab2bb3Spatrick map_->acquire(this);
1563cab2bb3Spatrick }
1573cab2bb3Spatrick
1583cab2bb3Spatrick template<typename T, uptr kSize>
Handle(AddrHashMap<T,kSize> * map,uptr addr,bool remove,bool create)1593cab2bb3Spatrick AddrHashMap<T, kSize>::Handle::Handle(AddrHashMap<T, kSize> *map, uptr addr,
1603cab2bb3Spatrick bool remove, bool create) {
1613cab2bb3Spatrick map_ = map;
1623cab2bb3Spatrick addr_ = addr;
1633cab2bb3Spatrick remove_ = remove;
1643cab2bb3Spatrick create_ = create;
1653cab2bb3Spatrick map_->acquire(this);
1663cab2bb3Spatrick }
1673cab2bb3Spatrick
1683cab2bb3Spatrick template<typename T, uptr kSize>
~Handle()1693cab2bb3Spatrick AddrHashMap<T, kSize>::Handle::~Handle() {
1703cab2bb3Spatrick map_->release(this);
1713cab2bb3Spatrick }
1723cab2bb3Spatrick
1733cab2bb3Spatrick template <typename T, uptr kSize>
1743cab2bb3Spatrick T *AddrHashMap<T, kSize>::Handle::operator->() {
1753cab2bb3Spatrick return &cell_->val;
1763cab2bb3Spatrick }
1773cab2bb3Spatrick
1783cab2bb3Spatrick template <typename T, uptr kSize>
1793cab2bb3Spatrick const T &AddrHashMap<T, kSize>::Handle::operator*() const {
1803cab2bb3Spatrick return cell_->val;
1813cab2bb3Spatrick }
1823cab2bb3Spatrick
1833cab2bb3Spatrick template <typename T, uptr kSize>
1843cab2bb3Spatrick T &AddrHashMap<T, kSize>::Handle::operator*() {
1853cab2bb3Spatrick return cell_->val;
1863cab2bb3Spatrick }
1873cab2bb3Spatrick
1883cab2bb3Spatrick template<typename T, uptr kSize>
created()1893cab2bb3Spatrick bool AddrHashMap<T, kSize>::Handle::created() const {
1903cab2bb3Spatrick return created_;
1913cab2bb3Spatrick }
1923cab2bb3Spatrick
1933cab2bb3Spatrick template<typename T, uptr kSize>
exists()1943cab2bb3Spatrick bool AddrHashMap<T, kSize>::Handle::exists() const {
1953cab2bb3Spatrick return cell_ != nullptr;
1963cab2bb3Spatrick }
1973cab2bb3Spatrick
1983cab2bb3Spatrick template<typename T, uptr kSize>
AddrHashMap()1993cab2bb3Spatrick AddrHashMap<T, kSize>::AddrHashMap() {
2003cab2bb3Spatrick table_ = (Bucket*)MmapOrDie(kSize * sizeof(table_[0]), "AddrHashMap");
2013cab2bb3Spatrick }
2023cab2bb3Spatrick
2033cab2bb3Spatrick template <typename T, uptr kSize>
acquire(Handle * h)204*810390e3Srobert void AddrHashMap<T, kSize>::acquire(Handle *h)
205*810390e3Srobert SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
2063cab2bb3Spatrick uptr addr = h->addr_;
2073cab2bb3Spatrick uptr hash = calcHash(addr);
2083cab2bb3Spatrick Bucket *b = &table_[hash];
2093cab2bb3Spatrick
2103cab2bb3Spatrick h->created_ = false;
2113cab2bb3Spatrick h->addidx_ = -1U;
2123cab2bb3Spatrick h->bucket_ = b;
2133cab2bb3Spatrick h->cell_ = nullptr;
2143cab2bb3Spatrick
2153cab2bb3Spatrick // If we want to remove the element, we need exclusive access to the bucket,
2163cab2bb3Spatrick // so skip the lock-free phase.
2173cab2bb3Spatrick if (h->remove_)
2183cab2bb3Spatrick goto locked;
2193cab2bb3Spatrick
2203cab2bb3Spatrick retry:
2213cab2bb3Spatrick // First try to find an existing element w/o read mutex.
2223cab2bb3Spatrick CHECK(!h->remove_);
2233cab2bb3Spatrick // Check the embed cells.
2243cab2bb3Spatrick for (uptr i = 0; i < kBucketSize; i++) {
2253cab2bb3Spatrick Cell *c = &b->cells[i];
2263cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_acquire);
2273cab2bb3Spatrick if (addr1 == addr) {
2283cab2bb3Spatrick h->cell_ = c;
2293cab2bb3Spatrick return;
2303cab2bb3Spatrick }
2313cab2bb3Spatrick }
2323cab2bb3Spatrick
2333cab2bb3Spatrick // Check the add cells with read lock.
2343cab2bb3Spatrick if (atomic_load(&b->add, memory_order_relaxed)) {
2353cab2bb3Spatrick b->mtx.ReadLock();
2363cab2bb3Spatrick AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
2373cab2bb3Spatrick for (uptr i = 0; i < add->size; i++) {
2383cab2bb3Spatrick Cell *c = &add->cells[i];
2393cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
2403cab2bb3Spatrick if (addr1 == addr) {
2413cab2bb3Spatrick h->addidx_ = i;
2423cab2bb3Spatrick h->cell_ = c;
2433cab2bb3Spatrick return;
2443cab2bb3Spatrick }
2453cab2bb3Spatrick }
2463cab2bb3Spatrick b->mtx.ReadUnlock();
2473cab2bb3Spatrick }
2483cab2bb3Spatrick
2493cab2bb3Spatrick locked:
2503cab2bb3Spatrick // Re-check existence under write lock.
2513cab2bb3Spatrick // Embed cells.
2523cab2bb3Spatrick b->mtx.Lock();
2533cab2bb3Spatrick for (uptr i = 0; i < kBucketSize; i++) {
2543cab2bb3Spatrick Cell *c = &b->cells[i];
2553cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
2563cab2bb3Spatrick if (addr1 == addr) {
2573cab2bb3Spatrick if (h->remove_) {
2583cab2bb3Spatrick h->cell_ = c;
2593cab2bb3Spatrick return;
2603cab2bb3Spatrick }
2613cab2bb3Spatrick b->mtx.Unlock();
2623cab2bb3Spatrick goto retry;
2633cab2bb3Spatrick }
2643cab2bb3Spatrick }
2653cab2bb3Spatrick
2663cab2bb3Spatrick // Add cells.
2673cab2bb3Spatrick AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed);
2683cab2bb3Spatrick if (add) {
2693cab2bb3Spatrick for (uptr i = 0; i < add->size; i++) {
2703cab2bb3Spatrick Cell *c = &add->cells[i];
2713cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
2723cab2bb3Spatrick if (addr1 == addr) {
2733cab2bb3Spatrick if (h->remove_) {
2743cab2bb3Spatrick h->addidx_ = i;
2753cab2bb3Spatrick h->cell_ = c;
2763cab2bb3Spatrick return;
2773cab2bb3Spatrick }
2783cab2bb3Spatrick b->mtx.Unlock();
2793cab2bb3Spatrick goto retry;
2803cab2bb3Spatrick }
2813cab2bb3Spatrick }
2823cab2bb3Spatrick }
2833cab2bb3Spatrick
2843cab2bb3Spatrick // The element does not exist, no need to create it if we want to remove.
2853cab2bb3Spatrick if (h->remove_ || !h->create_) {
2863cab2bb3Spatrick b->mtx.Unlock();
2873cab2bb3Spatrick return;
2883cab2bb3Spatrick }
2893cab2bb3Spatrick
2903cab2bb3Spatrick // Now try to create it under the mutex.
2913cab2bb3Spatrick h->created_ = true;
2923cab2bb3Spatrick // See if we have a free embed cell.
2933cab2bb3Spatrick for (uptr i = 0; i < kBucketSize; i++) {
2943cab2bb3Spatrick Cell *c = &b->cells[i];
2953cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
2963cab2bb3Spatrick if (addr1 == 0) {
2973cab2bb3Spatrick h->cell_ = c;
2983cab2bb3Spatrick return;
2993cab2bb3Spatrick }
3003cab2bb3Spatrick }
3013cab2bb3Spatrick
3023cab2bb3Spatrick // Store in the add cells.
3033cab2bb3Spatrick if (!add) {
3043cab2bb3Spatrick // Allocate a new add array.
3053cab2bb3Spatrick const uptr kInitSize = 64;
3063cab2bb3Spatrick add = (AddBucket*)InternalAlloc(kInitSize);
3073cab2bb3Spatrick internal_memset(add, 0, kInitSize);
3083cab2bb3Spatrick add->cap = (kInitSize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
3093cab2bb3Spatrick add->size = 0;
3103cab2bb3Spatrick atomic_store(&b->add, (uptr)add, memory_order_relaxed);
3113cab2bb3Spatrick }
3123cab2bb3Spatrick if (add->size == add->cap) {
3133cab2bb3Spatrick // Grow existing add array.
3143cab2bb3Spatrick uptr oldsize = sizeof(*add) + (add->cap - 1) * sizeof(add->cells[0]);
3153cab2bb3Spatrick uptr newsize = oldsize * 2;
3163cab2bb3Spatrick AddBucket *add1 = (AddBucket*)InternalAlloc(newsize);
3173cab2bb3Spatrick internal_memset(add1, 0, newsize);
3183cab2bb3Spatrick add1->cap = (newsize - sizeof(*add)) / sizeof(add->cells[0]) + 1;
3193cab2bb3Spatrick add1->size = add->size;
3203cab2bb3Spatrick internal_memcpy(add1->cells, add->cells, add->size * sizeof(add->cells[0]));
3213cab2bb3Spatrick InternalFree(add);
3223cab2bb3Spatrick atomic_store(&b->add, (uptr)add1, memory_order_relaxed);
3233cab2bb3Spatrick add = add1;
3243cab2bb3Spatrick }
3253cab2bb3Spatrick // Store.
3263cab2bb3Spatrick uptr i = add->size++;
3273cab2bb3Spatrick Cell *c = &add->cells[i];
3283cab2bb3Spatrick CHECK_EQ(atomic_load(&c->addr, memory_order_relaxed), 0);
3293cab2bb3Spatrick h->addidx_ = i;
3303cab2bb3Spatrick h->cell_ = c;
3313cab2bb3Spatrick }
3323cab2bb3Spatrick
3333cab2bb3Spatrick template <typename T, uptr kSize>
release(Handle * h)334*810390e3Srobert void AddrHashMap<T, kSize>::release(Handle *h)
335*810390e3Srobert SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
3363cab2bb3Spatrick if (!h->cell_)
3373cab2bb3Spatrick return;
3383cab2bb3Spatrick Bucket *b = h->bucket_;
3393cab2bb3Spatrick Cell *c = h->cell_;
3403cab2bb3Spatrick uptr addr1 = atomic_load(&c->addr, memory_order_relaxed);
3413cab2bb3Spatrick if (h->created_) {
3423cab2bb3Spatrick // Denote completion of insertion.
3433cab2bb3Spatrick CHECK_EQ(addr1, 0);
3443cab2bb3Spatrick // After the following store, the element becomes available
3453cab2bb3Spatrick // for lock-free reads.
3463cab2bb3Spatrick atomic_store(&c->addr, h->addr_, memory_order_release);
3473cab2bb3Spatrick b->mtx.Unlock();
3483cab2bb3Spatrick } else if (h->remove_) {
3493cab2bb3Spatrick // Denote that the cell is empty now.
3503cab2bb3Spatrick CHECK_EQ(addr1, h->addr_);
3513cab2bb3Spatrick atomic_store(&c->addr, 0, memory_order_release);
3523cab2bb3Spatrick // See if we need to compact the bucket.
3533cab2bb3Spatrick AddBucket *add = (AddBucket *)atomic_load(&b->add, memory_order_relaxed);
3543cab2bb3Spatrick if (h->addidx_ == -1U) {
3553cab2bb3Spatrick // Removed from embed array, move an add element into the freed cell.
3563cab2bb3Spatrick if (add && add->size != 0) {
3573cab2bb3Spatrick uptr last = --add->size;
3583cab2bb3Spatrick Cell *c1 = &add->cells[last];
3593cab2bb3Spatrick c->val = c1->val;
3603cab2bb3Spatrick uptr addr1 = atomic_load(&c1->addr, memory_order_relaxed);
3613cab2bb3Spatrick atomic_store(&c->addr, addr1, memory_order_release);
3623cab2bb3Spatrick atomic_store(&c1->addr, 0, memory_order_release);
3633cab2bb3Spatrick }
3643cab2bb3Spatrick } else {
3653cab2bb3Spatrick // Removed from add array, compact it.
3663cab2bb3Spatrick uptr last = --add->size;
3673cab2bb3Spatrick Cell *c1 = &add->cells[last];
3683cab2bb3Spatrick if (c != c1) {
3693cab2bb3Spatrick *c = *c1;
3703cab2bb3Spatrick atomic_store(&c1->addr, 0, memory_order_relaxed);
3713cab2bb3Spatrick }
3723cab2bb3Spatrick }
3733cab2bb3Spatrick if (add && add->size == 0) {
3743cab2bb3Spatrick // FIXME(dvyukov): free add?
3753cab2bb3Spatrick }
3763cab2bb3Spatrick b->mtx.Unlock();
3773cab2bb3Spatrick } else {
3783cab2bb3Spatrick CHECK_EQ(addr1, h->addr_);
3793cab2bb3Spatrick if (h->addidx_ != -1U)
3803cab2bb3Spatrick b->mtx.ReadUnlock();
3813cab2bb3Spatrick }
3823cab2bb3Spatrick }
3833cab2bb3Spatrick
3843cab2bb3Spatrick template<typename T, uptr kSize>
calcHash(uptr addr)3853cab2bb3Spatrick uptr AddrHashMap<T, kSize>::calcHash(uptr addr) {
3863cab2bb3Spatrick addr += addr << 10;
3873cab2bb3Spatrick addr ^= addr >> 6;
3883cab2bb3Spatrick return addr % kSize;
3893cab2bb3Spatrick }
3903cab2bb3Spatrick
3913cab2bb3Spatrick } // namespace __sanitizer
3923cab2bb3Spatrick
3933cab2bb3Spatrick #endif // SANITIZER_ADDRHASHMAP_H
394