Tervel  1.0.0
A collection of wait-free containers and algorithms.
popback_op.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_CONTAINERS_WF_VECTOR_POP_OP_H
26 #define __TERVEL_CONTAINERS_WF_VECTOR_POP_OP_H
27 
28 
29 #include <tervel/util/info.h>
30 #include <tervel/util/descriptor.h>
34 
35 
36 #include <tervel/containers/wf/vector/vector.hpp>
37 
38 namespace tervel {
39 namespace containers {
40 namespace wf {
41 namespace vector {
42 
43 template<typename T>
45 
46 template<typename T>
48 
49 
50 template<typename T>
52  public:
53  static constexpr PopOpHelper<T> * is_empty_const {reinterpret_cast<PopOpHelper<T> *>(0x1L)};
54 
55  PopOp(Vector<T> *vec)
56  : vec_(vec) {}
57 
58  ~PopOp() {
59  PopOpHelper<T> * temp = helper_.load();
60  assert(temp != nullptr);
61  if (temp != is_empty_const) {
63  }
64 
65  }
66 
67  static bool execute(Vector<T> *vec, T &val) {
68  size_t placed_pos = vec->size();
69  if (placed_pos == 0) {
70  return false;
71  }
72 
74  PopOpHelper<T> >(vec);
75  T help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
76  helper->set_control_word();
77 
78  std::atomic<T> *spot = vec->internal_array.get_spot(placed_pos);
79 
80  T current = spot->load();
81 
83  while (!progAssur.isDelayed()) {
84  if (current == Vector<T>::c_not_value_) {
85 
86  // else placed_pos > 0
87  helper->set_prev_spot(vec->internal_array.get_spot(placed_pos-1));
88  if (!spot->compare_exchange_strong(current, help_t)) {
89  continue;
90  }
91  helper->complete(reinterpret_cast<void *>(help_t),
92  reinterpret_cast< std::atomic<void *> *>(spot));
93  bool op_res = helper->result(val);
95 
96  if (op_res) {
97  return true;
98  } else { // it failed
99  placed_pos--;
100  if (placed_pos == 0) {
101  return false;
102  }
103 
105  PopOpHelper<T> >(vec);
106  help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
107  helper->set_control_word();
108  // It was not placed correctly, so the vector must have shrunk
109 
110  spot = vec->internal_array.get_spot(placed_pos);
111  current = spot->load();
112  }
113  } else if (vec->internal_array.is_descriptor(current, spot)) {
114  continue;
115  } else { // its a valid value
116  placed_pos++;
117  spot = vec->internal_array.get_spot(placed_pos);
118  current = spot->load();
119  }
120  } // while not complete
121 
122  util::memory::rc::free_descriptor(helper, true);
123 
124  // std::cout << "Wait-Free Mode" << std::endl;
125  // Wait-Free code
126  PopOp<T> * op = new PopOp<T>(vec);
128  tervel::util::OpRecord *>(op));
129  bool op_res = op->result(val);
130  op->safe_delete();
131 
132  return op_res;
133  };
134 
135  bool result(T &val) {
136  PopOpHelper<T> *temp = helper_.load();
137  assert(temp != nullptr);
138  if (temp == is_empty_const) {
139  return false;
140  } else {
141  return temp->result(val);
142  }
143  }
144 
145  bool result() {
146  PopOpHelper<T> *temp = helper_.load();
147  assert(temp != nullptr);
148  if (temp == is_empty_const) {
149  return false;
150  } else {
151  return temp->result();
152  }
153  }
154 
155  void set_failed() {
156  PopOpHelper<T> *temp = helper_.load();
157  if (temp == nullptr) {
158  helper_.compare_exchange_strong(temp, is_empty_const);
159  }
160  }
161 
162  void help_complete() {
163  size_t placed_pos = vec_->size();
164  if (placed_pos == 0) {
165  this->set_failed();
166  return;
167  }
169  PopOpHelper<T> >(vec_, this);
170  T help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
172 
173 
174 
175  std::atomic<T> *spot = vec_->internal_array.get_spot(placed_pos);
176  T current = spot->load();
177 
178  while (helper_.load() == nullptr) {
179  if (current == Vector<T>::c_not_value_) {
180 
181  // else place_pos > 0
182 
183  helper->set_prev_spot(vec_->internal_array.get_spot(placed_pos-1));
184 
185  if (!spot->compare_exchange_strong(current, help_t)) {
186  continue;
187  }
188  helper->complete(reinterpret_cast<void *>(help_t),
189  reinterpret_cast< std::atomic<void *> *>(spot));
190 
191  bool op_res = helper->result();
192  if (op_res) {
193  return;
194  } else {
196 
197  placed_pos--;
198  if (placed_pos == 0) {
199  this->set_failed();
200  return;
201  }
202 
204  PopOpHelper<T> >(vec_, this);
205  help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
206 
207  // It was not placed correctly, the vector must have shrunk
208 
209  spot = vec_->internal_array.get_spot(placed_pos);
210  current = spot->load();
211  }
212  } else if (vec_->internal_array.is_descriptor(current, spot)) {
213  continue;
214  } else { // its a valid value
215  placed_pos++;
216  spot = vec_->internal_array.get_spot(placed_pos);
217  current = spot->load();
218  }
219  } // while not complete
220 
221  util::memory::rc::free_descriptor(helper, true);
222  };
223 
224  bool is_watched() {
225  PopOpHelper<T> * temp = helper_.load();
226 
227  if (temp == nullptr) {
228  assert(false); // THis state should not be reached
229  return false;
230  } else if (this->is_failed()) {
231  return false;
232  }
234  }
235 
236  private:
237  friend class PopOpHelper<T>;
238  Vector<T> *vec_;
239  std::atomic<T> * prev_spot_ {nullptr};
240  std::atomic<PopOpHelper<T> *> helper_ {nullptr};
241 }; // class PopOp
242 
243 template<typename T>
244 class PopOpHelper: public tervel::util::Descriptor {
245  public:
246  static constexpr PopOpSubHelper<T> * fail_const {reinterpret_cast<PopOpSubHelper<T> *>(0x1L)};
247 
248 
249  PopOpHelper(Vector<T> * vec, PopOp<T> *op)
250  : vec_(vec)
251  , op_(op) {}
252 
253  explicit PopOpHelper(Vector<T> * vec)
254  : vec_(vec)
255  , op_{nullptr} {}
256 
258  PopOpSubHelper<T> * temp = child_.load();
259  if (temp == nullptr) {
260  return;
261  } else if (temp == fail_const) {
262  return;
263  } else {
265  }
266  }
267 
268 
269  void set_prev_spot(std::atomic<T> * prev_spot) {
270  prev_spot_ = prev_spot;
271  }
272 
273  bool in_progress() {
274  return child_.load() == nullptr;
275  }
276 
277  bool result(T &val) {
278  PopOpSubHelper<T> *temp = child_.load();
279  assert(temp != nullptr);
280  if (temp == fail_const) {
281  return false;
282  } else {
283  temp->val(val);
284  return true;
285  }
286  }
287 
288  bool result() {
289  PopOpSubHelper<T> *temp = child_.load();
290  assert(temp != nullptr);
291  if (temp == fail_const) {
292  return false;
293  } else {
294  return true;
295  }
296  }
297 
298  void fail() {
299  PopOpSubHelper<T> *temp = child_.load();
300  if (temp == nullptr) {
301  child_.compare_exchange_strong(temp, fail_const);
302  }
303  }
304 
305 
307  void * get_logical_value() {
308  return reinterpret_cast<void *>(Vector<T>::c_not_value_);
309  }
310 
312  void * complete(void *value, std::atomic<void *> *address) {
313  std::atomic<T> * spot = reinterpret_cast<std::atomic<T> *>(address);
314  T help_t = reinterpret_cast<T>(util::memory::rc::mark_first(this));
315 
316  T current_prev = prev_spot_->load();
317 
318 
319 
321  while (in_progress()) {
322  if (progAssur.isDelayed() || current_prev == Vector<T>::c_not_value_) {
323  fail();
324  break;
325  } else if (vec_->internal_array.is_descriptor(current_prev, prev_spot_)) {
326  continue;
327  } else {
329  PopOpSubHelper<T> >(this, current_prev);
330  T child_m = reinterpret_cast<T>(util::memory::rc::mark_first(child));
331 
332  if (prev_spot_->compare_exchange_strong(current_prev, child_m)) {
333  child->complete(reinterpret_cast<void *>(child_m),
334  reinterpret_cast<std::atomic<void *> *>(prev_spot_));
335 
336  if (child_.load() != child) {
337  util::memory::rc::free_descriptor(child, false);
338  }
339  break;
340  } else {
342  continue;
343  }
344  }
345  } // end while
346 
347  if (spot->compare_exchange_strong(help_t,
348  reinterpret_cast<T>(Vector<T>::c_not_value_))) {
349  return reinterpret_cast<void *>(Vector<T>::c_not_value_);
350  } else {
351  return reinterpret_cast<void *>(help_t);
352  }
353  } // complete
354 
355  bool associate(PopOpSubHelper<T> * child) {
356  PopOpSubHelper<T> * cur_child = child_.load();
357  if (cur_child == nullptr) {
358  if (child_.compare_exchange_strong(cur_child, child)) {
359  cur_child = child;
360  }
361  }
362 
363  if (cur_child != child) {
364  return false;
365  }
366 
367  if (op_ == nullptr) {
368  return true;
369  }
370 
371  PopOpHelper<T> *temp_null = nullptr;
372  bool res = op_->helper_.compare_exchange_strong(temp_null, this);
373  if (res || temp_null == this) {
374  assert(op_->helper_.load() == this);
375  return true;
376  } else {
377  assert(op_->helper_.load() != this);
378  return false;
379  }
380  };
381 
391  bool on_watch(std::atomic<void *> *address, void * value) {
392  if (op_ == nullptr) {
393  return true;
394  }
397  t_SlotID::SHORTUSE, op_, address, value);
398 
399  return success;
400  };
401 
403  void on_unwatch() {
404  if (op_ != nullptr) {
406  util::memory::hp::HazardPointer::unwatch(t_SlotID::SHORTUSE);
407  }
408  };
409 
411  bool on_is_watched() {
412  PopOpSubHelper<T> *temp = child_.load();
413  assert(temp != nullptr);
414  if (temp == fail_const) {
415  return false;
416  } else {
418  }
419  };
420 
422  tervel::tl_control_word = &child_;
423  }
424 
425 
426  private:
427  Vector<T> *vec_;
428  PopOp<T> *op_ {nullptr};
429  std::atomic<T> * prev_spot_ {nullptr};
430  std::atomic<PopOpSubHelper<T> *> child_ {nullptr};
431 };
432 
433 template<typename T>
434 class PopOpSubHelper: public tervel::util::Descriptor {
435  public:
436  explicit PopOpSubHelper(PopOpHelper<T> *parent, T val)
437  : val_(val)
438  , parent_(parent) {}
439 
440  void val(T &val) {
441  val = val_;
442  };
443 
445  void * get_logical_value() {
446  return reinterpret_cast<void *>(Vector<T>::c_not_value_);
447  }
448 
450  void * complete(void *value, std::atomic<void *> *address) {
451  assert(value == util::memory::rc::mark_first(this));
452 
453  bool is_valid = parent_->associate(this);
454 
455  void * new_val;
456  if (is_valid) {
457  new_val = reinterpret_cast<void *>(Vector<T>::c_not_value_);
458  } else {
459  new_val = reinterpret_cast<void *>(val_);
460  }
461 
462  if (address->compare_exchange_strong(value, new_val)) {
463  return value;
464  } else {
465  return new_val;
466  }
467  } // complete
468 
478  bool on_watch(std::atomic<void *> *address, void * value) {
479  assert(value == util::memory::rc::mark_first(this));
480  if (tervel::util::memory::rc::watch(parent_, address, value)) {
481  complete(value, address);
483  }
484  return false;
485  };
486 
487 
488  private:
489  T val_;
491 };
492 
493 
494 } // namespace vector
495 } // namespace wf
496 } // namespace containers
497 } // namespace tervel
498 #endif // __TERVEL_CONTAINERS_WF_VECTOR_POP_OP_H
DescrType * get_descriptor(Args &&...args)
Constructs and returns a descriptor.
Definition: descriptor_util.h:50
virtual void * get_logical_value()=0
This method is implemented by each sub class.
__thread void * tl_control_word
virtual void on_unwatch()
This method must be implemented if on_watch is implemented, and is optional otherwise.
Definition: descriptor.h:109
void * mark_first(tervel::util::Descriptor *descr)
This returns the passed reference with its least signifcant bit set to 1.
Definition: descriptor_util.h:157
void * complete(void *value, std::atomic< void * > *address)
This method is implemented by each sub class and must guarantee that upon return that the descriptor ...
Definition: popback_op.h:312
bool on_watch(std::atomic< void * > *address, void *value)
This method is optional to implement for each sub class.
Definition: popback_op.h:391
Vector< T > * vec_
Definition: popback_op.h:238
~PopOpHelper()
Definition: popback_op.h:257
void free_descriptor(tervel::util::Descriptor *descr, bool dont_check=false)
Once a user is done with a descriptor, they should free it with this method.
Definition: descriptor_util.h:65
void set_failed()
Definition: popback_op.h:155
bool result()
Definition: popback_op.h:145
TODO(steven):
Definition: mcas.h:36
void unwatch(tervel::util::Descriptor *descr)
This method is used to decrement the reference count of the passed descriptor object.
Definition: descriptor_util.h:139
std::atomic< T > * prev_spot_
Definition: popback_op.h:239
void * get_logical_value()
This method is implemented by each sub class.
Definition: popback_op.h:445
void set_control_word()
Definition: popback_op.h:421
PopOpHelper(Vector< T > *vec, PopOp< T > *op)
Definition: popback_op.h:249
bool result(T &val)
Definition: popback_op.h:135
virtual bool on_is_watched()
This method is optional to implement for each sub class.
Definition: descriptor.h:117
bool result()
Definition: popback_op.h:288
static bool execute(Vector< T > *vec, T &val)
Definition: popback_op.h:67
PopOpHelper< T > * parent_
Definition: popback_op.h:490
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
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
void set_prev_spot(std::atomic< T > *prev_spot)
Definition: popback_op.h:269
PopOpSubHelper(PopOpHelper< T > *parent, T val)
Definition: popback_op.h:436
This defines the Descriptor class, this class is designed to be extend and be used in conjunction wit...
Definition: descriptor.h:60
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.
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.
virtual void * complete(void *current, std::atomic< void * > *address)=0
This method is implemented by each sub class and must guarantee that upon return that the descriptor ...
void safe_delete(bool no_check=false, ElementList *const element_list=tervel::tl_thread_info->get_hp_element_list())
This function is used to free a hazard pointer protected object if it is safe to do so OR add it to a...
Definition: hp_element.h:67
bool is_watched()
Definition: popback_op.h:224
bool watch(tervel::util::Descriptor *descr, std::atomic< void * > *address, void *value)
This method is used to increment the reference count of the passed descriptor object.
Definition: descriptor_util.h:105
This class is used to create Operation Records.
Definition: progress_assurance.h:52
PopOp(Vector< T > *vec)
Definition: popback_op.h:55
bool on_watch(std::atomic< void * > *address, void *value)
This method is optional to implement for each sub class.
Definition: popback_op.h:478
Vector< T > * vec_
Definition: popback_op.h:427
virtual bool on_watch(std::atomic< void * > *, void *)
This method is optional to implement for each sub class.
Definition: descriptor.h:99
void * get_logical_value()
This method is implemented by each sub class.
Definition: popback_op.h:307
~PopOp()
Definition: popback_op.h:58
void val(T &val)
Definition: popback_op.h:440
void help_complete()
Implementations of this function that upon its return the operation described in the OpRecord has bee...
Definition: popback_op.h:162
Definition: popback_op.h:51
void fail()
Definition: popback_op.h:298
bool in_progress()
Definition: popback_op.h:273
bool isDelayed(size_t val=1)
Definition: progress_assurance.h:125
std::atomic< PopOpHelper< T > * > helper_
Definition: popback_op.h:240
Definition: progress_assurance.h:120
PopOpHelper(Vector< T > *vec)
Definition: popback_op.h:253
bool on_is_watched()
This method is optional to implement for each sub class.
Definition: popback_op.h:411
static constexpr PopOpHelper< T > * is_empty_const
Definition: popback_op.h:53
bool associate(PopOpSubHelper< T > *child)
Definition: popback_op.h:355
void * complete(void *value, std::atomic< void * > *address)
This method is implemented by each sub class and must guarantee that upon return that the descriptor ...
Definition: popback_op.h:450
void on_unwatch()
This method must be implemented if on_watch is implemented, and is optional otherwise.
Definition: popback_op.h:403
bool result(T &val)
Definition: popback_op.h:277
SlotID
Definition: hazard_pointer.h:58