Tervel  1.0.0
A collection of wait-free containers and algorithms.
wf_hash_map_no_delete.h
Go to the documentation of this file.
1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 2015 University of Central Florida's Computer Software Engineering
5 Scalable & Secure Systems (CSE - S3) Lab
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 THE SOFTWARE.
24 */
25 #ifndef TERVEL_CONTAINER_WF_HASH_MAP_WFHM_HASHMAP_H
26 #define TERVEL_CONTAINER_WF_HASH_MAP_WFHM_HASHMAP_H
27 
28 #include <stdlib.h>
29 #include <atomic>
30 #include <cmath>
31 
32 // TODO(Steven):
33 //
34 // Document code
35 //
36 // Implement Progress Assurance.
37 //
38 // Test get_position function on keys > 64 bits
39 // Stronger Correctness Tests
40 //
41 namespace tervel {
42 
43 namespace util {
44  int round_to_next_power_of_two(uint64_t value) {
45  double val = std::log2(value);
46  int int_val = static_cast<int>(val);
47  if (int_val < val) {
48  int_val++;
49  }
50  return int_val;
51  };
52 }
53 
54 namespace containers {
55 namespace wf {
60 template<class Key, class Value>
61 struct default_functor {
62  Key hash(Key k) {
63  return k;
64  }
65 
66  bool key_equals(Key a, Key b) {
67  return a == b;
68  }
69 };
70 
71 
72 
84 template< class Key, class Value, class Functor = default_functor<Key, Value> >
86  public:
87  class ValueAccessor;
88 
89  HashMapNoDelete(uint64_t capacity, uint64_t expansion_rate = 3)
92  , secondary_array_size_(std::pow(2, expansion_rate))
93  , secondary_array_pow_(expansion_rate)
95 
96  size_.store(0);
97  for (size_t i = 0; i < primary_array_size_; i++) {
98  primary_array_[i].store(nullptr);
99  }
100 
101  }
102 
111  for (size_t i = 0; i < primary_array_size_; i++) {
112  Node * temp = primary_array_[i].load();
113  if (temp != nullptr) {
114  delete temp;
115  }
116  }
117  } // ~ HashMap
118 
119 
120 
135  bool at(Key key, ValueAccessor &va);
136 
149  bool insert(Key key, Value value);
150 
151 
155  size_t size() {
156  return size_.load();
157  };
158 
159 
164  public:
165  friend class HashMapNoDelete;
167  : value_(nullptr) {}
168 
170 
175  return value_;
176  };
177 
181  bool valid() {
182  return (value_ != nullptr);
183  };
184 
188  void reset() {
189  value_ = nullptr;
190  };
191 
192  private:
197  void init(Value * value) {
198  value_ = value;
199  };
200 
201  Value * value_;
202  };
203 
204 
210  uint64_t get_position(Key &key, size_t depth);
211 
216  uint64_t max_depth();
217 
223  void print_key(Key &key);
224 
225  private:
226  class Node;
227  friend class Node;
228  typedef std::atomic<Node *> Location;
229 
230 
234  class Node {
235  public:
236  Node() {}
237  virtual ~Node() {}
238 
242  virtual bool is_array() = 0;
243 
247  virtual bool is_data() = 0;
248  };
249 
253  class ArrayNode : public Node {
254  public:
255  explicit ArrayNode(uint64_t len)
256  : len_(len)
257  , internal_array_(new Location[len]()) {
258  for (size_t i = 0; i < len_; i++) {
259  internal_array_[i].store(nullptr);
260  }
261  }
262 
267  for (size_t i = 0; i < len_; i++) {
268  Node * temp = internal_array_[i].load();
269  if (temp != nullptr) {
270  delete temp;
271  }
272  }
273  } // ~ArrayNode
274 
275 
280  Location *access(uint64_t pos) {
281  assert(pos < len_ && pos >=0);
282  return &(internal_array_[pos]);
283  }
284 
288  bool is_array() {
289  return true;
290  }
291 
295  bool is_data() {
296  return false;
297  }
298 
299  private:
300  uint64_t len_;
301  std::unique_ptr<Location[]> internal_array_;
302  };
303 
308  class DataNode : public Node {
309  public:
310  explicit DataNode(Key k, Value v)
311  : key_(k)
312  , value_(v) {}
313 
314  ~DataNode() { }
315 
319  bool is_array() {
320  return false;
321  }
322 
326  bool is_data() {
327  return true;
328  }
329 
330  Key key_;
332  };
333 
341  void expand_map(Location * loc, Node * &curr_value, uint64_t next_position);
342 
343 
344 
345  const size_t primary_array_size_;
346  const size_t primary_array_pow_;
347  const size_t secondary_array_size_;
348  const size_t secondary_array_pow_;
349 
350  std::atomic<uint64_t> size_;
351 
352  std::unique_ptr<Location[]> primary_array_;
353 }; // class wf hash map
354 
355 
356 template<class Key, class Value, class Functor>
358 at(Key key, ValueAccessor &va) {
359  Functor functor;
360  key = functor.hash(key);
361 
362  size_t depth = 0;
363  uint64_t position = get_position(key, depth);
364  Location *loc = &(primary_array_[position]);
365  Node *curr_value;
366 
367  bool op_res = false;
368  while (true) {
369  curr_value = loc->load();
370 
371  if (curr_value == nullptr) {
372  break;
373  } else if (curr_value->is_array()) {
374  ArrayNode * array_node = reinterpret_cast<ArrayNode *>(curr_value);
375  depth++;
376  position = get_position(key, depth);
377  loc = array_node->access(position);
378  continue;
379  } else {
380  assert(curr_value->is_data());
381  DataNode * data_node = reinterpret_cast<DataNode *>(curr_value);
382 
383  if (functor.key_equals(data_node->key_, key)) {
384  va.init(&(data_node->value_));
385  op_res = true;
386  }
387  break;
388  }
389  } // while true
390 
391  return op_res;
392 } // at
393 
394 
395 template<class Key, class Value, class Functor>
397 insert(Key key, Value value) {
398  Functor functor;
399  key = functor.hash(key);
400 
401  DataNode * new_node = new DataNode(key, value);
402 
403  size_t depth = 0;
404  uint64_t position = get_position(key, depth);
405  Location *loc = &(primary_array_[position]);
406  Node *curr_value = loc->load();
407 
408  bool op_res;
409  while (true) {
410 
411  if (curr_value == nullptr) {
412  if (loc->compare_exchange_strong(curr_value, new_node)) {
413  size_.fetch_add(1);
414  op_res = true;
415  break;
416  }
417  } else if (curr_value->is_array()) {
418  ArrayNode * array_node = reinterpret_cast<ArrayNode *>(curr_value);
419  depth++;
420  position = get_position(key, depth);
421  loc = array_node->access(position);
422  curr_value = loc->load();
423  } else { // it is a data node
424  assert(curr_value->is_data());
425  DataNode * data_node = reinterpret_cast<DataNode *>(curr_value);
426 
427  if (functor.key_equals(data_node->key_, key)) {
428  op_res = false;
429  break;
430  } else {
431  // Key differs, needs to expand...
432  const uint64_t next_position = get_position(data_node->key_, depth+1);
433  expand_map(loc, curr_value, next_position);
434  } // else key differs
435  } // else it is a data node
436  } // while true
437 
438  if (!op_res) {
439  assert(loc->load() != new_node);
440  delete new_node;
441  }
442  return op_res;
443 } // insert
444 
445 
446 template<class Key, class Value, class Functor>
448 expand_map(Location * loc, Node * &curr_value, uint64_t next_position) {
449  assert(curr_value->is_data());
450 
451  ArrayNode * array_node = new ArrayNode(secondary_array_size_);
452  array_node->access(next_position)->store(curr_value);
453  assert(array_node->is_array());
454 
455  if (loc->compare_exchange_strong(curr_value, array_node)) {
456  return;
457  } else {
458  assert(loc->load() != array_node);
459  array_node->access(next_position)->store(nullptr);
460  delete array_node;
461  }
462 } // expand
463 
464 template<class Key, class Value, class Functor>
466 get_position(Key &key, size_t depth) {
467  const uint64_t *long_array = reinterpret_cast<uint64_t *>(&key);
468  const size_t max_length = sizeof(Key) / (64 / 8);
469 
470  assert(depth <= max_depth());
471  if (depth == 0) {
472  // We need the first 'primary_array_pow_' bits
473  assert(primary_array_pow_ < 64);
474 
475  uint64_t position = long_array[0] >> (64 - primary_array_pow_);
476 
477  assert(position < primary_array_size_);
478  return position;
479  } else {
480  const int start_bit_offset = (depth-1)*secondary_array_pow_ +
481  primary_array_pow_; // Inclusive
482  const int end_bit_offset = (depth)*secondary_array_pow_ +
483  primary_array_pow_; // Not inclusive
484 
485  const size_t start_idx = start_bit_offset / 64;
486  const size_t start_idx_offset = start_bit_offset % 64;
487  const size_t end_idx = end_bit_offset / 64;
488  const size_t end_idx_offset = end_bit_offset % 64;
489 
490  assert(start_idx == end_idx || start_idx + 1 == end_idx);
491  assert(end_idx <= max_length);
492 
493  // TODO(steven): add 0 padding to fill extra bits if the bits don't
494  // divide evenly.
495  if (start_idx == end_idx) {
496  uint64_t value = long_array[start_idx];
497  value = value << start_idx_offset;
498  value = value >> (64 - secondary_array_pow_);
499 
500  assert(value < secondary_array_size_);
501  return value;
502  } else {
503  uint64_t value = long_array[start_idx];
504  value = value << start_idx_offset;
505  value = value >> (64 - secondary_array_pow_ + end_idx_offset);
506  value = value << (end_idx_offset);
507 
508 
509  uint64_t value2;
510  if (end_idx == max_length) {
511  value2 = 0;
512  } else {
513  value2 = long_array[end_idx];
514  }
515  value2 = value2 >> (64 - end_idx_offset);
516 
517  uint64_t position = (value | value2);
518  assert(position < secondary_array_size_);
519  return position;
520  }
521  }
522 } // get_position
523 
524 template<class Key, class Value, class Functor>
527  uint64_t max_depth = sizeof(Key)*8;
528  max_depth -= primary_array_pow_;
529  max_depth = std::ceil(max_depth / secondary_array_pow_);
530  max_depth++;
531 
532  return max_depth;
533 }
534 
535 template<class Key, class Value, class Functor>
537 print_key(Key &key) {
538  size_t max_depth_ = max_depth();
539  std::cout << "K(" << key << ") :";
540  for (size_t i = 0; i <= max_depth_; i++) {
541  uint64_t temp = get_position(key, i);
542  std::cout << temp << "-";
543  }
544  std::cout << "\n" << std::endl;
545 } // print_key
546 
547 } // namespace wf
548 } // namespace containers
549 } // namespace tervel
550 
551 #endif // TERVEL_CONTAINER_WF_HASH_MAP_WFHM_HASHMAP_H
Key hash(Key k)
Definition: wf_hash_map_no_delete.h:62
const size_t primary_array_size_
Definition: wf_hash_map_no_delete.h:345
const size_t secondary_array_size_
Definition: wf_hash_map_no_delete.h:347
const size_t primary_array_pow_
Definition: wf_hash_map_no_delete.h:346
Value value_
Definition: wf_hash_map_no_delete.h:331
uint64_t get_position(Key &key, size_t depth)
Definition: wf_hash_map_no_delete.h:466
std::atomic< Node * > Location
Definition: wf_hash_map_no_delete.h:228
This class is used to hold the secondary array structure.
Definition: wf_hash_map_no_delete.h:253
std::unique_ptr< Location[]> internal_array_
Definition: wf_hash_map_no_delete.h:301
TODO(steven):
Definition: mcas.h:36
STL namespace.
bool is_data()
Definition: wf_hash_map_no_delete.h:326
~HashMapNoDelete()
Not Thread Safe! If it is a node type then the node will be freed If it is an array node then the des...
Definition: wf_hash_map_no_delete.h:110
bool key_equals(Key a, Key b)
Definition: wf_hash_map_no_delete.h:66
~ValueAccessor()
Definition: wf_hash_map_no_delete.h:169
virtual ~Node()
Definition: wf_hash_map_no_delete.h:237
bool valid()
Definition: wf_hash_map_no_delete.h:181
DataNode(Key k, Value v)
Definition: wf_hash_map_no_delete.h:310
int round_to_next_power_of_two(uint64_t value)
Returns the next power of two.
Definition: wf_hash_map_no_delete.h:44
HashMapNoDelete(uint64_t capacity, uint64_t expansion_rate=3)
Definition: wf_hash_map_no_delete.h:89
~DataNode()
Definition: wf_hash_map_no_delete.h:314
uint64_t max_depth()
Definition: wf_hash_map_no_delete.h:526
size_t size()
Definition: wf_hash_map_no_delete.h:155
~ArrayNode()
See Notes on hash map destructor.
Definition: wf_hash_map_no_delete.h:266
Key key_
Definition: wf_hash_map_no_delete.h:330
std::unique_ptr< Location[]> primary_array_
Definition: wf_hash_map_no_delete.h:352
uint64_t len_
Definition: wf_hash_map_no_delete.h:300
bool at(Key key, ValueAccessor &va)
This function returns true and initializes the passed ValueAccessor if the key exists in the hash map...
Definition: wf_hash_map_no_delete.h:358
void print_key(Key &key)
Outputs the positions a key belongs in at each depth.
Definition: wf_hash_map_no_delete.h:537
Value * value()
Definition: wf_hash_map_no_delete.h:174
Value * value_
Definition: wf_hash_map_no_delete.h:199
A wait-free hash map implementation.
Definition: wf_hash_map_no_delete.h:85
const size_t secondary_array_pow_
Definition: wf_hash_map_no_delete.h:348
uint64_t Value
Definition: testObject.h:59
This class is used to hold a key and value pair.
Definition: wf_hash_map_no_delete.h:308
This class is used to safe guard access to values.
Definition: wf_hash_map_no_delete.h:163
This class is used to differentiate between data_nodes and array_nodes/.
Definition: wf_hash_map_no_delete.h:234
bool is_array()
Definition: wf_hash_map_no_delete.h:288
Location * access(uint64_t pos)
Definition: wf_hash_map_no_delete.h:280
void init(Value *value)
Initializes the value accessors.
Definition: wf_hash_map_no_delete.h:197
bool insert(Key key, Value value)
This function returns true if the key value pair was successfully inserted.
Definition: wf_hash_map_no_delete.h:397
Node()
Definition: wf_hash_map_no_delete.h:236
bool is_array()
Definition: wf_hash_map_no_delete.h:319
void expand_map(Location *loc, Node *&curr_value, uint64_t next_position)
Increases the capacity of the hash map by replacing a data node reference with a reference to an arra...
Definition: wf_hash_map_no_delete.h:448
void reset()
Resets this value accessors,clearing the variables.
Definition: wf_hash_map_no_delete.h:188
std::atomic< uint64_t > size_
Definition: wf_hash_map_no_delete.h:350
bool is_data()
Definition: wf_hash_map_no_delete.h:295
ValueAccessor()
Definition: wf_hash_map_no_delete.h:166
ArrayNode(uint64_t len)
Definition: wf_hash_map_no_delete.h:255