Apollo  6.0
Open source self driving car software
atomic_hash_map.h
Go to the documentation of this file.
1 /******************************************************************************
2  * Copyright 2018 The Apollo Authors. All Rights Reserved.
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 #ifndef CYBER_BASE_ATOMIC_HASH_MAP_H_
18 #define CYBER_BASE_ATOMIC_HASH_MAP_H_
19 
20 #include <atomic>
21 #include <cstdint>
22 #include <type_traits>
23 #include <utility>
24 
25 namespace apollo {
26 namespace cyber {
27 namespace base {
36 template <typename K, typename V, std::size_t TableSize = 128,
38  (TableSize & (TableSize - 1)) == 0,
39  int>::type = 0>
41  public:
42  AtomicHashMap() : capacity_(TableSize), mode_num_(capacity_ - 1) {}
43  AtomicHashMap(const AtomicHashMap &other) = delete;
44  AtomicHashMap &operator=(const AtomicHashMap &other) = delete;
45 
46  bool Has(K key) {
47  uint64_t index = key & mode_num_;
48  return table_[index].Has(key);
49  }
50 
51  bool Get(K key, V **value) {
52  uint64_t index = key & mode_num_;
53  return table_[index].Get(key, value);
54  }
55 
56  bool Get(K key, V *value) {
57  uint64_t index = key & mode_num_;
58  V *val = nullptr;
59  bool res = table_[index].Get(key, &val);
60  if (res) {
61  *value = *val;
62  }
63  return res;
64  }
65 
66  void Set(K key) {
67  uint64_t index = key & mode_num_;
68  table_[index].Insert(key);
69  }
70 
71  void Set(K key, const V &value) {
72  uint64_t index = key & mode_num_;
73  table_[index].Insert(key, value);
74  }
75 
76  void Set(K key, V &&value) {
77  uint64_t index = key & mode_num_;
78  table_[index].Insert(key, std::forward<V>(value));
79  }
80 
81  private:
82  struct Entry {
83  Entry() {}
84  explicit Entry(K key) : key(key) {
85  value_ptr.store(new V(), std::memory_order_release);
86  }
87  Entry(K key, const V &value) : key(key) {
88  value_ptr.store(new V(value), std::memory_order_release);
89  }
90  Entry(K key, V &&value) : key(key) {
91  value_ptr.store(new V(std::forward<V>(value)), std::memory_order_release);
92  }
93  ~Entry() { delete value_ptr.load(std::memory_order_acquire); }
94 
95  K key = 0;
96  std::atomic<V *> value_ptr = {nullptr};
97  std::atomic<Entry *> next = {nullptr};
98  };
99 
100  class Bucket {
101  public:
102  Bucket() : head_(new Entry()) {}
103  ~Bucket() {
104  Entry *ite = head_;
105  while (ite) {
106  auto tmp = ite->next.load(std::memory_order_acquire);
107  delete ite;
108  ite = tmp;
109  }
110  }
111 
112  bool Has(K key) {
113  Entry *m_target = head_->next.load(std::memory_order_acquire);
114  while (Entry *target = m_target) {
115  if (target->key < key) {
116  m_target = target->next.load(std::memory_order_acquire);
117  continue;
118  } else {
119  return target->key == key;
120  }
121  }
122  return false;
123  }
124 
125  bool Find(K key, Entry **prev_ptr, Entry **target_ptr) {
126  Entry *prev = head_;
127  Entry *m_target = head_->next.load(std::memory_order_acquire);
128  while (Entry *target = m_target) {
129  if (target->key == key) {
130  *prev_ptr = prev;
131  *target_ptr = target;
132  return true;
133  } else if (target->key > key) {
134  *prev_ptr = prev;
135  *target_ptr = target;
136  return false;
137  } else {
138  prev = target;
139  m_target = target->next.load(std::memory_order_acquire);
140  }
141  }
142  *prev_ptr = prev;
143  *target_ptr = nullptr;
144  return false;
145  }
146 
147  void Insert(K key, const V &value) {
148  Entry *prev = nullptr;
149  Entry *target = nullptr;
150  Entry *new_entry = nullptr;
151  V *new_value = nullptr;
152  while (true) {
153  if (Find(key, &prev, &target)) {
154  // key exists, update value
155  if (!new_value) {
156  new_value = new V(value);
157  }
158  auto old_val_ptr = target->value_ptr.load(std::memory_order_acquire);
159  if (target->value_ptr.compare_exchange_strong(
160  old_val_ptr, new_value, std::memory_order_acq_rel,
161  std::memory_order_relaxed)) {
162  delete old_val_ptr;
163  if (new_entry) {
164  delete new_entry;
165  new_entry = nullptr;
166  }
167  return;
168  }
169  continue;
170  } else {
171  if (!new_entry) {
172  new_entry = new Entry(key, value);
173  }
174  new_entry->next.store(target, std::memory_order_release);
175  if (prev->next.compare_exchange_strong(target, new_entry,
176  std::memory_order_acq_rel,
177  std::memory_order_relaxed)) {
178  // Insert success
179  if (new_value) {
180  delete new_value;
181  new_value = nullptr;
182  }
183  return;
184  }
185  // another entry has been inserted, retry
186  }
187  }
188  }
189 
190  void Insert(K key, V &&value) {
191  Entry *prev = nullptr;
192  Entry *target = nullptr;
193  Entry *new_entry = nullptr;
194  V *new_value = nullptr;
195  while (true) {
196  if (Find(key, &prev, &target)) {
197  // key exists, update value
198  if (!new_value) {
199  new_value = new V(std::forward<V>(value));
200  }
201  auto old_val_ptr = target->value_ptr.load(std::memory_order_acquire);
202  if (target->value_ptr.compare_exchange_strong(
203  old_val_ptr, new_value, std::memory_order_acq_rel,
204  std::memory_order_relaxed)) {
205  delete old_val_ptr;
206  if (new_entry) {
207  delete new_entry;
208  new_entry = nullptr;
209  }
210  return;
211  }
212  continue;
213  } else {
214  if (!new_entry) {
215  new_entry = new Entry(key, value);
216  }
217  new_entry->next.store(target, std::memory_order_release);
218  if (prev->next.compare_exchange_strong(target, new_entry,
219  std::memory_order_acq_rel,
220  std::memory_order_relaxed)) {
221  // Insert success
222  if (new_value) {
223  delete new_value;
224  new_value = nullptr;
225  }
226  return;
227  }
228  // another entry has been inserted, retry
229  }
230  }
231  }
232 
233  void Insert(K key) {
234  Entry *prev = nullptr;
235  Entry *target = nullptr;
236  Entry *new_entry = nullptr;
237  V *new_value = nullptr;
238  while (true) {
239  if (Find(key, &prev, &target)) {
240  // key exists, update value
241  if (!new_value) {
242  new_value = new V();
243  }
244  auto old_val_ptr = target->value_ptr.load(std::memory_order_acquire);
245  if (target->value_ptr.compare_exchange_strong(
246  old_val_ptr, new_value, std::memory_order_acq_rel,
247  std::memory_order_relaxed)) {
248  delete old_val_ptr;
249  if (new_entry) {
250  delete new_entry;
251  new_entry = nullptr;
252  }
253  return;
254  }
255  continue;
256  } else {
257  if (!new_entry) {
258  new_entry = new Entry(key);
259  }
260  new_entry->next.store(target, std::memory_order_release);
261  if (prev->next.compare_exchange_strong(target, new_entry,
262  std::memory_order_acq_rel,
263  std::memory_order_relaxed)) {
264  // Insert success
265  if (new_value) {
266  delete new_value;
267  new_value = nullptr;
268  }
269  return;
270  }
271  // another entry has been inserted, retry
272  }
273  }
274  }
275 
276  bool Get(K key, V **value) {
277  Entry *prev = nullptr;
278  Entry *target = nullptr;
279  if (Find(key, &prev, &target)) {
280  *value = target->value_ptr.load(std::memory_order_acquire);
281  return true;
282  }
283  return false;
284  }
285 
286  Entry *head_;
287  };
288 
289  private:
290  Bucket table_[TableSize];
291  uint64_t capacity_;
292  uint64_t mode_num_;
293 };
294 
295 } // namespace base
296 } // namespace cyber
297 } // namespace apollo
298 
299 #endif // CYBER_BASE_ATOMIC_HASH_MAP_H_
void Set(K key, const V &value)
Definition: atomic_hash_map.h:71
PlanningContext is the runtime context in planning. It is persistent across multiple frames...
Definition: atomic_hash_map.h:25
bool Get(K key, V **value)
Definition: atomic_hash_map.h:51
AtomicHashMap & operator=(const AtomicHashMap &other)=delete
bool Get(K key, V *value)
Definition: atomic_hash_map.h:56
A implementation of lock-free fixed size hash map.
Definition: atomic_hash_map.h:40
void Set(K key)
Definition: atomic_hash_map.h:66
bool Has(K key)
Definition: atomic_hash_map.h:46
apollo::cyber::base::std value
void Set(K key, V &&value)
Definition: atomic_hash_map.h:76
AtomicHashMap()
Definition: atomic_hash_map.h:42