Tervel  1.0.0
A collection of wait-free containers and algorithms.
wf_hash_map.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 
29 #include <tervel/util/info.h>
33 
34 // TODO(Steven):
35 //
36 // Document code
37 //
38 // Implement Progress Assurance.
39 //
40 // Test get_position function on keys > 64 bits
41 // Stronger Correctness Tests
42 //
43 namespace tervel {
44 namespace containers {
45 namespace wf {
50 template<class Key, class Value>
52  Key hash(Key k) {
53  return k;
54  }
55 
56  bool key_equals(Key a, Key b) {
57  return a == b;
58  }
59 };
60 
61 
62 
74 template< class Key, class Value, class Functor = default_functor<Key, Value> >
75 class HashMap {
76  public:
77  class ValueAccessor;
78 
79  HashMap(uint64_t capacity, uint64_t expansion_rate = 3)
82  , secondary_array_size_(std::pow(2, expansion_rate))
83  , secondary_array_pow_(expansion_rate)
85 
96  for (size_t i = 0; i < primary_array_size_; i++) {
97  Node * temp = primary_array_[i].load();
98  if (temp != nullptr) {
99  delete temp;
100  }
101  }
102  } // ~ HashMap
103 
104 
105 
120  bool at(Key key, ValueAccessor &va);
121 
134  bool insert(Key key, Value value);
135 
144  bool remove(Key key);
145 
149  size_t size() {
150  return size_.load();
151  };
152 
153 
160  public:
161  friend class HashMap;
163  : access_count_(nullptr)
164  , value_(nullptr) {}
165 
167  reset();
168  }
169 
174  return value_;
175  }
176 
180  bool valid() {
181  return (value_ != nullptr && access_count_ != nullptr);
182  }
183 
188  void reset() {
189  if (access_count_) {
190  access_count_->fetch_add(-1);
191  access_count_ = nullptr;
192  value_ = nullptr;
193  }
194  }
195 
196  private:
202  void init(Value * value, std::atomic<int64_t> *access_count) {
203  if (access_count_) { // In case they reuse the object
204  reset();
205  }
206 
207  value_ = value;
208  access_count_ = access_count;
209  }
210 
211  std::atomic<int64_t> *access_count_;
213  };
214 
215 
221  uint64_t get_position(Key &key, size_t depth);
222 
227  uint64_t max_depth();
228 
233  void print_key(Key &key);
234 
235  private:
236  class Node;
237  friend class Node;
238  friend class ForceExpandOp;
239  typedef std::atomic<Node *> Location;
240 
241 
245  class Node {
246  public:
247  Node() {}
248  virtual ~Node() {}
249 
253  virtual bool is_array() = 0;
254 
258  virtual bool is_data() = 0;
259  };
260 
264  class ArrayNode : public Node {
265  public:
266  explicit ArrayNode(uint64_t len)
267  : len_(len)
268  , internal_array_(new Location[len]()) {
269  for (size_t i = 0; i < len_; i++) {
270  internal_array_[i].store(nullptr);
271  }
272  }
273 
278  for (size_t i = 0; i < len_; i++) {
279  Node * temp = internal_array_[i].load();
280  if (temp != nullptr) {
281  delete temp;
282  }
283  }
284  } // ~ArrayNode
285 
286 
291  Location *access(uint64_t pos) {
292  assert(pos < len_ && pos >=0);
293  return &(internal_array_[pos]);
294  }
295 
299  bool is_array() {
300  return true;
301  }
302 
306  bool is_data() {
307  return false;
308  }
309 
310  private:
311  uint64_t len_;
312  std::unique_ptr<Location[]> internal_array_;
313  };
314 
320  public:
321  explicit DataNode(Key k, Value v)
322  : key_(k)
323  , value_(v)
324  , access_count_(0) {}
325 
326  ~DataNode() { }
327 
331  bool is_array() {
332  return false;
333  }
334 
338  bool is_data() {
339  return true;
340  }
341 
342  Key key_;
344  std::atomic<int64_t> access_count_;
345  };
346 
347 
353  public:
354  ForceExpandOp(HashMap *map, Location *loc, size_t depth)
355  : map_(map)
356  , loc_(loc)
357  , depth_(depth){}
358 
359  void help_complete() {
360  if (depth_ >= map_->max_depth()) {
361  return;
362  }
363  Node *value = loc_->load();
364  while (true) {
365  if (!map_->hp_watch_and_get_value(loc_,value)) {
366  continue;
367  } else if (value == nullptr || value->is_data()) {
368  map_->expand_map(loc_, value, depth_);
369  value = loc_->load();
370  } else {
371  break;
372  }
373  }
374  };
375 
376  private:
377  friend class HashMap;
378  HashMap *map_{nullptr};
379  Location *loc_{nullptr};
380  size_t depth_{0};
381  };
382 
390  void expand_map(Location * loc, Node * curr_value, size_t depth);
391 
401  bool hp_watch_and_get_value(Location * loc, Node * &value);
402  void hp_unwatch();
403 
404  const size_t primary_array_size_;
405  const size_t primary_array_pow_;
406  const size_t secondary_array_size_;
407  const size_t secondary_array_pow_;
408 
409  std::atomic<uint64_t> size_;
410 
411  std::unique_ptr<Location[]> primary_array_;
412 }; // class wf hash map
413 
414 template<class Key, class Value, class Functor>
417  std::atomic<void *> *temp_address =
418  reinterpret_cast<std::atomic<void *> *>(loc);
419 
420  void * temp = temp_address->load();
421 
422  if (temp == nullptr) {
423  value = nullptr;
424  return true;
425  }
426 
429  temp, temp_address, temp);
430 
431  if (is_watched) {
432  value = reinterpret_cast<Node *>(temp);
433  }
434  return is_watched;
435 } // hp_watch_and_get_value
436 
437 template<class Key, class Value, class Functor>
442 } // hp_unwatch
443 
444 
445 template<class Key, class Value, class Functor>
447 at(Key key, ValueAccessor &va) {
448  Functor functor;
449  key = functor.hash(key);
450 
451  size_t depth = 0;
452  uint64_t position = get_position(key, depth);
453  Location *loc = &(primary_array_[position]);
454  Node *curr_value;
455 
456 
457  bool op_res = false;
458 
460  while (true) {
461  if (progAssur.isDelayed()) {
462  ForceExpandOp *op = new ForceExpandOp(this, loc, depth);
464  reinterpret_cast<tervel::util::OpRecord *>(op));
465  progAssur.reset();
466  continue;
467  }
468 
469  if (!hp_watch_and_get_value(loc, curr_value)) {
470  continue;
471  } else if (curr_value == nullptr) {
472  break;
473  } else if (curr_value->is_array()) {
474  ArrayNode * array_node = reinterpret_cast<ArrayNode *>(curr_value);
475  depth++;
476  position = get_position(key, depth);
477  loc = array_node->access(position);
478  continue;
479  } else {
480  assert(curr_value->is_data());
481  DataNode * data_node = reinterpret_cast<DataNode *>(curr_value);
482 
483  if (functor.key_equals(data_node->key_, key)) {
484  int64_t res = data_node->access_count_.fetch_add(1);
485  if (res >= 0) { // its not deleted.
486  va.init(&(data_node->value_), &(data_node->access_count_));
487  op_res = true;
488  } else {
489  data_node->access_count_.fetch_add(-1);
490  op_res = false;
491  }
492  }
493  break;
494  }
495  } // while true
496 
497  hp_unwatch();
498  return op_res;
499 } // at
500 
501 
502 template<class Key, class Value, class Functor>
504 insert(Key key, Value value) {
506 
507  Functor functor;
508  key = functor.hash(key);
509 
510  DataNode * new_node = new DataNode(key, value);
511 
513  size_t depth = 0;
514  uint64_t position = get_position(key, depth);
515 
516  Location *loc = &(primary_array_[position]);
517  Node *curr_value;
518 
519  bool op_res;
520  while (true) {
521  if (progAssur.isDelayed()) {
522  // TODO(steven): implement an op record.
523  // util::ProgressAssurance::make_announcement(
524  // reinterpret_cast<tervel::util::OpRecord *>(op));
525  progAssur.reset();
526  continue;
527  }
528 
529  if (!hp_watch_and_get_value(loc, curr_value)) {
530  continue;
531  } else if (curr_value == nullptr) {
532  if (loc->compare_exchange_strong(curr_value, new_node)) {
533  size_.fetch_add(1);
534  op_res = true;
535  break;
536  }
537  } else if (curr_value->is_array()) {
538  ArrayNode * array_node = reinterpret_cast<ArrayNode *>(curr_value);
539  depth++;
540  position = get_position(key, depth);
541  loc = array_node->access(position);
542  } else { // it is a data node
543  assert(curr_value->is_data());
544  DataNode * data_node = reinterpret_cast<DataNode *>(curr_value);
545 
546  if (data_node->access_count_.load() < 0) {
547  if (loc->compare_exchange_strong(curr_value, new_node)) {
548  hp_unwatch();
549  data_node->safe_delete();
550  size_.fetch_add(1);
551  op_res = true;
552  break;
553  }
554  } else if (functor.key_equals(data_node->key_, key)) {
555  op_res = false;
556  break;
557  } else {
558  // Key differs, needs to expand...
559  expand_map(loc, curr_value, depth);
560  } // else key differs
561  } // else it is a data node
562  } // while true
563 
564  hp_unwatch();
565 
566  if (!op_res) {
567  assert(loc->load() != new_node);
568  delete new_node;
569  }
570  return op_res;
571 } // insert
572 
573 
574 template<class Key, class Value, class Functor>
576 remove(Key key) {
577  Functor functor;
578  key = functor.hash(key);
579 
580  size_t depth = 0;
581  uint64_t position = get_position(key, depth);
582 
583  Location *loc = &(primary_array_[position]);
584  Node *curr_value;
585 
586 
588 
589  bool op_res = false;
590  while (true) {
591  if (progAssur.isDelayed()) {
592  // TODO(Steven): implement op record
593  // util::ProgressAssurance::make_announcement(
594  // reinterpret_cast<tervel::util::OpRecord *>(op));
595  progAssur.reset();
596  continue;
597  }
598 
599  if (!hp_watch_and_get_value(loc, curr_value)) {
600  continue;
601  } else if (curr_value == nullptr) {
602  op_res = false;
603  break;
604  } else if (curr_value->is_array()) {
605  ArrayNode * array_node = reinterpret_cast<ArrayNode *>(curr_value);
606  depth++;
607  position = get_position(key, depth);
608  loc = array_node->access(position);
609  continue;
610  } else { // it is a data node
611  assert(curr_value->is_data());
612  DataNode *data_node = reinterpret_cast<DataNode *>(curr_value);
613  int64_t temp_expected = 0;
614  if (functor.key_equals(data_node->key_, key) &&
615  data_node->access_count_.compare_exchange_strong(temp_expected,
617  ) {
618  op_res = true;
619  size_.fetch_add(-1);
620  // data_node is a key match, value match, and we set it to dead
621  if (loc->compare_exchange_strong(curr_value, nullptr)) {
622  assert(loc->load() != data_node);
623  assert(data_node->access_count_.load() < 0);
624  hp_unwatch();
625  data_node->safe_delete();
626  } else {
627  // It is logically deleted, and some other thread will/has already
628  // will delete it.
629  }
630  }
631  break;
632  } // it is a data node
633  } // while
634 
635  hp_unwatch();
636  return op_res;
637 } // remove
638 
639 
640 template<class Key, class Value, class Functor>
642 expand_map(Location * loc, Node * curr_value, size_t depth) {
643 
644  uint64_t next_position = 0;
645 
646  ArrayNode * array_node = new ArrayNode(secondary_array_size_);
647  if (curr_value != nullptr) {
648  assert(curr_value->is_data());
649  DataNode *data_node = reinterpret_cast<DataNode *>(curr_value);
650  next_position = get_position(data_node->key_, depth+1);
651  array_node->access(next_position)->store(curr_value);
652  }
653  assert(array_node->is_array());
654 
655  if (loc->compare_exchange_strong(curr_value, array_node)) {
656  return;
657  } else {
658  assert(loc->load() != array_node);
659  array_node->access(next_position)->store(nullptr);
660  delete array_node;
661  }
662 } // expand
663 
664 template<class Key, class Value, class Functor>
666 get_position(Key &key, size_t depth) {
667  const uint64_t *long_array = reinterpret_cast<uint64_t *>(&key);
668  const size_t max_length = sizeof(Key) / (64 / 8);
669 
670  assert(depth <= max_depth());
671  if (depth == 0) {
672  // We need the first 'primary_array_pow_' bits
673  assert(primary_array_pow_ < 64);
674 
675  uint64_t position = long_array[0] >> (64 - primary_array_pow_);
676 
677  assert(position < primary_array_size_);
678  return position;
679  } else {
680  const int start_bit_offset = (depth-1)*secondary_array_pow_ +
681  primary_array_pow_; // Inclusive
682  const int end_bit_offset = (depth)*secondary_array_pow_ +
683  primary_array_pow_; // Not inclusive
684 
685  const size_t start_idx = start_bit_offset / 64;
686  const size_t start_idx_offset = start_bit_offset % 64;
687  const size_t end_idx = end_bit_offset / 64;
688  const size_t end_idx_offset = end_bit_offset % 64;
689 
690  assert(start_idx == end_idx || start_idx + 1 == end_idx);
691  assert(end_idx <= max_length);
692 
693  // TODO(steven): add 0 padding to fill extra bits if the bits don't
694  // divide evenly.
695  if (start_idx == end_idx) {
696  uint64_t value = long_array[start_idx];
697  value = value << start_idx_offset;
698  value = value >> (64 - secondary_array_pow_);
699 
700  assert(value < secondary_array_size_);
701  return value;
702  } else {
703  uint64_t value = long_array[start_idx];
704  value = value << start_idx_offset;
705  value = value >> (64 - secondary_array_pow_ + end_idx_offset);
706  value = value << (end_idx_offset);
707 
708 
709  uint64_t value2;
710  if (end_idx == max_length) {
711  value2 = 0;
712  } else {
713  value2 = long_array[end_idx];
714  }
715  value2 = value2 >> (64 - end_idx_offset);
716 
717  uint64_t position = (value | value2);
718  assert(position < secondary_array_size_);
719  return position;
720  }
721  }
722 } // get_position
723 
724 template<class Key, class Value, class Functor>
727  uint64_t max_depth = sizeof(Key)*8;
728  max_depth -= primary_array_pow_;
729  max_depth = std::ceil(max_depth / secondary_array_pow_);
730  max_depth++;
731 
732  return max_depth;
733 }
734 
735 template<class Key, class Value, class Functor>
737 print_key(Key &key) {
738  size_t max_depth_ = max_depth();
739  std::cout << "K(" << key << ") :";
740  for (size_t i = 0; i <= max_depth_; i++) {
741  uint64_t temp = get_position(key, i);
742  std::cout << temp << "-";
743  }
744  std::cout << "\n" << std::endl;
745 } // print_key
746 
747 } // namespace wf
748 } // namespace containers
749 } // namespace tervel
750 
751 #endif // TERVEL_CONTAINER_WF_HASH_MAP_WFHM_HASHMAP_H
uint64_t len_
Definition: wf_hash_map.h:311
This class is used to safe guard access to values.
Definition: wf_hash_map.h:159
const size_t secondary_array_size_
Definition: wf_hash_map.h:406
static void check_for_announcement(ProgressAssurance *const progress_assuarance=nullptr)
This function checks at most one position in the op_table_ for an OPRecod If one is found it will cal...
Definition: progress_assurance.h:151
Location * access(uint64_t pos)
Definition: wf_hash_map.h:291
void init(Value *value, std::atomic< int64_t > *access_count)
Initializes the value accessor.
Definition: wf_hash_map.h:202
Key hash(Key k)
Definition: wf_hash_map.h:52
uint64_t get_position(Key &key, size_t depth)
Definition: wf_hash_map.h:666
void expand_map(Location *loc, Node *curr_value, size_t depth)
Increases the capacity of the hash map by replacing a data node reference with a reference to an arra...
Definition: wf_hash_map.h:642
virtual ~Node()
Definition: wf_hash_map.h:248
void hp_unwatch()
Definition: wf_hash_map.h:439
size_t depth_
Definition: wf_hash_map.h:380
bool hp_watch_and_get_value(Location *loc, Node *&value)
This is a wrapper for hazard pointers.
Definition: wf_hash_map.h:416
TODO(steven): add description TODO(steven): move into a file.
Definition: wf_hash_map.h:352
~ValueAccessor()
Definition: wf_hash_map.h:166
size_t size()
Definition: wf_hash_map.h:149
bool remove(Key key)
Attempts to remove a key/value pair from the hash map Returns false in the event the key is not in th...
Definition: wf_hash_map.h:576
const size_t primary_array_pow_
Definition: wf_hash_map.h:405
TODO(steven):
Definition: mcas.h:36
bool is_array()
Definition: wf_hash_map.h:299
STL namespace.
Node()
Definition: wf_hash_map.h:247
void reset()
Resets this value accessors, decrementing the access_count and clearing the variables.
Definition: wf_hash_map.h:188
HashMap(uint64_t capacity, uint64_t expansion_rate=3)
Definition: wf_hash_map.h:79
A default Functor implementation.
Definition: wf_hash_map.h:51
bool is_data()
Definition: wf_hash_map.h:338
bool key_equals(Key a, Key b)
Definition: wf_hash_map.h:56
Location * loc_
Definition: wf_hash_map.h:379
HashMap * map_
Definition: wf_hash_map.h:378
Key key_
Definition: wf_hash_map.h:342
This class is used for the creation of Hazard Pointer Protected Objects Objects which extend it have ...
Definition: hp_element.h:53
Value * value_
Definition: wf_hash_map.h:212
bool is_array()
Definition: wf_hash_map.h:331
static void make_announcement(OpRecord *op, const uint64_t tid=tervel::tl_thread_info->get_thread_id(), ProgressAssurance *const prog_assur=tervel::tl_thread_info->get_progress_assurance())
This function places the.
Definition: progress_assurance.h:172
int round_to_next_power_of_two(uint64_t value)
Returns the next power of two.
Definition: wf_hash_map_no_delete.h:44
bool is_watched(tervel::util::Descriptor *descr)
This method is used to determine if the passed descriptor is under rc protection. ...
Definition: descriptor_util.h:78
const uint64_t get_num_threads()
~ArrayNode()
See Notes on hash map destructor.
Definition: wf_hash_map.h:277
bool insert(Key key, Value value)
This function returns true if the key value pair was successfully inserted.
Definition: wf_hash_map.h:504
This class is used to differentiate between data_nodes and array_nodes/.
Definition: wf_hash_map.h:245
__thread ThreadContext * tl_thread_info
Value * value()
Definition: wf_hash_map.h:173
static void unwatch(SlotID slot_id, HazardPointer *const hazard_pointer=tervel::tl_thread_info->get_hazard_pointer())
This method is used to remove the hazard pointer watch.
DataNode(Key k, Value v)
Definition: wf_hash_map.h:321
static bool watch(SlotID slot_id, Element *elem, std::atomic< void * > *address, void *expected, HazardPointer *const hazard_pointer=tervel::tl_thread_info->get_hazard_pointer())
This method is used to achieve a hazard pointer watch on the the based descr.
const size_t secondary_array_pow_
Definition: wf_hash_map.h:407
~DataNode()
Definition: wf_hash_map.h:326
const size_t primary_array_size_
Definition: wf_hash_map.h:404
std::atomic< int64_t > * access_count_
Definition: wf_hash_map.h:211
This class is used to create Operation Records.
Definition: progress_assurance.h:52
friend class Node
Definition: wf_hash_map.h:236
This class is used to hold a key and value pair.
Definition: wf_hash_map.h:319
std::atomic< int64_t > access_count_
Definition: wf_hash_map.h:344
~HashMap()
Not Thread Safe! May create a very large stack! Note: should implement a better way...
Definition: wf_hash_map.h:95
Value value_
Definition: wf_hash_map.h:343
uint64_t Value
Definition: testObject.h:59
This class is used to hold the secondary array structure.
Definition: wf_hash_map.h:264
bool valid()
Definition: wf_hash_map.h:180
void print_key(Key &key)
Outputs the positions a key belongs in at each depth.
Definition: wf_hash_map.h:737
ForceExpandOp(HashMap *map, Location *loc, size_t depth)
Definition: wf_hash_map.h:354
A wait-free hash map implementation.
Definition: wf_hash_map.h:75
bool is_data()
Definition: wf_hash_map.h:306
void help_complete()
Implementations of this function that upon its return the operation described in the OpRecord has bee...
Definition: wf_hash_map.h:359
std::unique_ptr< Location[]> primary_array_
Definition: wf_hash_map.h:411
bool isDelayed(size_t val=1)
Definition: progress_assurance.h:125
ArrayNode(uint64_t len)
Definition: wf_hash_map.h:266
std::unique_ptr< Location[]> internal_array_
Definition: wf_hash_map.h:312
Definition: progress_assurance.h:120
std::atomic< Node * > Location
Definition: wf_hash_map.h:239
uint64_t max_depth()
Definition: wf_hash_map.h:726
std::atomic< uint64_t > size_
Definition: wf_hash_map.h:409
ValueAccessor()
Definition: wf_hash_map.h:162
void reset(size_t limit=TERVEL_PROG_ASSUR_LIMIT)
Definition: progress_assurance.h:131
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.h:447