6079b060527ec23cdd30e65f94013d012814258c
[folly.git] / folly / test / AtomicHashArrayTest.cpp
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <sys/mman.h>
18
19 #include <cstddef>
20 #include <map>
21 #include <stdexcept>
22
23 #include "folly/AtomicHashArray.h"
24 #include "folly/Hash.h"
25 #include "folly/Conv.h"
26 #include "folly/Memory.h"
27 #include <gtest/gtest.h>
28
29 #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
30 #define MAP_ANONYMOUS MAP_ANON
31 #endif
32
33 using namespace std;
34 using namespace folly;
35
36 template <class T>
37 class MmapAllocator {
38  public:
39   typedef T value_type;
40   typedef T* pointer;
41   typedef const T* const_pointer;
42   typedef T& reference;
43   typedef const T& const_reference;
44
45   typedef ptrdiff_t difference_type;
46   typedef size_t size_type;
47
48   T* address(T& x) const {
49     return std::addressof(x);
50   }
51
52   const T* address(const T& x) const {
53     return std::addressof(x);
54   }
55
56   size_t max_size() const {
57     return std::numeric_limits<size_t>::max();
58   }
59
60   template <class U> struct rebind {
61     typedef MmapAllocator<U> other;
62   };
63
64   bool operator!=(const MmapAllocator<T>& other) const {
65     return !(*this == other);
66   }
67
68   bool operator==(const MmapAllocator<T>& other) const {
69     return true;
70   }
71
72   template <class... Args>
73   void construct(T* p, Args&&... args) {
74     new (p) T(std::forward<Args>(args)...);
75   }
76
77   void destroy(T* p) {
78     p->~T();
79   }
80
81   T *allocate(size_t n) {
82     void *p = mmap(nullptr, n * sizeof(T), PROT_READ | PROT_WRITE,
83         MAP_SHARED | MAP_ANONYMOUS, -1, 0);
84     if (!p) throw std::bad_alloc();
85     return (T *)p;
86   }
87
88   void deallocate(T *p, size_t n) {
89     munmap(p, n * sizeof(T));
90   }
91 };
92
93 template<class KeyT, class ValueT>
94 pair<KeyT,ValueT> createEntry(int i) {
95   return pair<KeyT,ValueT>(to<KeyT>(folly::hash::jenkins_rev_mix32(i) % 1000),
96                            to<ValueT>(i + 3));
97 }
98
99 template<class KeyT, class ValueT, class Allocator = std::allocator<char>>
100 void testMap() {
101   typedef AtomicHashArray<KeyT, ValueT, std::hash<KeyT>,
102                           std::equal_to<KeyT>, Allocator> MyArr;
103   auto arr = MyArr::create(150);
104   map<KeyT, ValueT> ref;
105   for (int i = 0; i < 100; ++i) {
106     auto e = createEntry<KeyT, ValueT>(i);
107     auto ret = arr->insert(e);
108     EXPECT_EQ(!ref.count(e.first), ret.second);  // succeed iff not in ref
109     ref.insert(e);
110     EXPECT_EQ(ref.size(), arr->size());
111     if (ret.first == arr->end()) {
112       EXPECT_FALSE("AHA should not have run out of space.");
113       continue;
114     }
115     EXPECT_EQ(e.first, ret.first->first);
116     EXPECT_EQ(ref.find(e.first)->second, ret.first->second);
117   }
118
119   for (int i = 125; i > 0; i -= 10) {
120     auto e = createEntry<KeyT, ValueT>(i);
121     auto ret = arr->erase(e.first);
122     auto refRet = ref.erase(e.first);
123     EXPECT_EQ(ref.size(), arr->size());
124     EXPECT_EQ(refRet, ret);
125   }
126
127   for (int i = 155; i > 0; i -= 10) {
128     auto e = createEntry<KeyT, ValueT>(i);
129     auto ret = arr->insert(e);
130     auto refRet = ref.insert(e);
131     EXPECT_EQ(ref.size(), arr->size());
132     EXPECT_EQ(*refRet.first, *ret.first);
133     EXPECT_EQ(refRet.second, ret.second);
134   }
135
136   for (const auto& e : ref) {
137     auto ret = arr->find(e.first);
138     if (ret == arr->end()) {
139       EXPECT_FALSE("Key was not in AHA");
140       continue;
141     }
142     EXPECT_EQ(e.first, ret->first);
143     EXPECT_EQ(e.second, ret->second);
144   }
145 }
146
147 template<class KeyT, class ValueT, class Allocator = std::allocator<char>>
148 void testNoncopyableMap() {
149   typedef AtomicHashArray<KeyT, std::unique_ptr<ValueT>, std::hash<KeyT>,
150                           std::equal_to<KeyT>, Allocator> MyArr;
151   auto arr = MyArr::create(150);
152   for (int i = 0; i < 100; i++) {
153     arr->insert(make_pair(i,std::unique_ptr<ValueT>(new ValueT(i))));
154   }
155   for (int i = 0; i < 100; i++) {
156     auto ret = arr->find(i);
157     EXPECT_EQ(*(ret->second), i);
158   }
159 }
160
161
162 TEST(Aha, InsertErase_i32_i32) {
163   testMap<int32_t, int32_t>();
164   testMap<int32_t, int32_t, MmapAllocator<char>>();
165   testNoncopyableMap<int32_t, int32_t>();
166   testNoncopyableMap<int32_t, int32_t, MmapAllocator<char>>();
167 }
168 TEST(Aha, InsertErase_i64_i32) {
169   testMap<int64_t, int32_t>();
170   testMap<int64_t, int32_t, MmapAllocator<char>>();
171   testNoncopyableMap<int64_t, int32_t>();
172   testNoncopyableMap<int64_t, int32_t, MmapAllocator<char>>();
173 }
174 TEST(Aha, InsertErase_i64_i64) {
175   testMap<int64_t, int64_t>();
176   testMap<int64_t, int64_t, MmapAllocator<char>>();
177   testNoncopyableMap<int64_t, int64_t>();
178   testNoncopyableMap<int64_t, int64_t, MmapAllocator<char>>();
179 }
180 TEST(Aha, InsertErase_i32_i64) {
181   testMap<int32_t, int64_t>();
182   testMap<int32_t, int64_t, MmapAllocator<char>>();
183   testNoncopyableMap<int32_t, int64_t>();
184   testNoncopyableMap<int32_t, int64_t, MmapAllocator<char>>();
185 }
186 TEST(Aha, InsertErase_i32_str) {
187   testMap<int32_t, string>();
188   testMap<int32_t, string, MmapAllocator<char>>();
189 }
190 TEST(Aha, InsertErase_i64_str) {
191   testMap<int64_t, string>();
192   testMap<int64_t, string, MmapAllocator<char>>();
193 }