Tervel  1.0.0
A collection of wait-free containers and algorithms.
pushback_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_PUSH_OP_H
26 #define __TERVEL_CONTAINERS_WF_VECTOR_PUSH_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>
47 class PushDescr;
48 
49 
50 template<typename T>
52  public:
53  PushOp(Vector<T> *vec, T val)
54  : vec_(vec)
55  , new_val_(val) {}
56 
57  ~PushOp() {
58  PushOpHelper<T> * temp = helper_.load();
59  assert(temp != nullptr);
61  }
62 
63  static size_t execute(Vector<T> *vec, T val) {
64 
65  size_t placed_pos = vec->size();
66 
67  std::atomic<T> *spot = vec->internal_array.get_spot(placed_pos);
68 
69  T current = spot->load();
70 
72  while (progAssur.isDelayed()) {
73  if (current == Vector<T>::c_not_value_) {
74  if (placed_pos == 0) {
75  if (spot->compare_exchange_strong(current, val)) {
76 
77  return 0;
78  } else {
79  continue;
80  }
81  }
82 
83 
85  PushDescr<T> >(val, vec);
86  T help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
87  helper->set_prev_spot(vec->internal_array.get_spot(placed_pos-1));
88 
89  if (!spot->compare_exchange_strong(current, help_t)) {
91  continue;
92  }
93  helper->complete(reinterpret_cast<void *>(help_t),
94  reinterpret_cast< std::atomic<void *> *>(spot));
95  bool op_res = helper->result();
97 
98  if (op_res) {
99  return placed_pos;
100  } else { // it failed
101  // It was not placed correctly, so the vector must have shrunk
102  placed_pos--;
103  spot = vec->internal_array.get_spot(placed_pos);
104  current = spot->load();
105  }
106  } else if (vec->internal_array.is_descriptor(current, spot)) {
107  assert((current & 2) == 0);
108  continue;
109  } else { // its a valid value
110  placed_pos++;
111  spot = vec->internal_array.get_spot(placed_pos);
112  current = spot->load();
113  }
114  } // while not complete
115 
116  // Wait-Free code
117 // std::cout << "WF mode ("<< val <<")" << std::endl;
118  PushOp<T> * pushOp = new PushOp<T>(vec, val);
120  tervel::util::OpRecord *>(pushOp));
121  placed_pos = pushOp->result();
122 
123 
124 
125  pushOp->safe_delete();
126 
127  return placed_pos;
128  };
129 
130  uint64_t result() {
131  PushOpHelper<T> *temp = helper_.load();
132  assert(temp != nullptr);
133  return temp->idx();
134  }
135 
136  void help_complete() {
138 
140  PushOpHelper<T> >(this);
141  T help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
142 
143 
144  size_t placed_pos = vec_->size();
145 
146  std::atomic<T> *spot = vec_->internal_array.get_spot(placed_pos);
147  T current = spot->load();
148 
149  while (helper_.load() == nullptr) {
150  if (current == Vector<T>::c_not_value_) {
151  helper->set_idx(placed_pos);
152  if (!spot->compare_exchange_strong(current, help_t)) {
153  continue;
154  }
155  helper->complete(reinterpret_cast<void *>(help_t),
156  reinterpret_cast< std::atomic<void *> *>(spot));
157 
158  bool op_res = helper->result();
159  if (op_res) {
160  return;
161  } else {
164  PushOpHelper<T> >(this);
165  help_t = reinterpret_cast<T>(util::memory::rc::mark_first(helper));
166 
167  // It was not placed correctly, the vector must have shrunk
168  placed_pos--;
169  spot = vec_->internal_array.get_spot(placed_pos);
170  current = spot->load();
171  }
172  } else if (vec_->internal_array.is_descriptor(current, spot)) {
173  continue;
174  } else { // its a valid value
175  placed_pos++;
176  spot = vec_->internal_array.get_spot(placed_pos);
177  current = spot->load();
178  }
179  } // while not complete
180 
181  util::memory::rc::free_descriptor(helper, true);
182  };
183 
184  bool is_watched() {
185  PushOpHelper<T> * temp = helper_.load();
186 
187  if (temp == nullptr) {
188  assert(false); // THis state should not be reached
189  return false;
190  }
191  return temp->is_watched();
192  }
193 
194  private:
195  friend class PushOpHelper<T>;
196  Vector<T> *vec_;
198  std::atomic<PushOpHelper<T> *> helper_ {nullptr};
199 }; // class PushOp
200 
201 template<typename T>
202 class PushDescr: public tervel::util::Descriptor {
203  public:
204  explicit PushDescr(T val, Vector<T> * vec)
205  : val_(val)
206  , vec_(vec) {}
207 
208  void set_prev_spot(std::atomic<T> * prev_spot) {
209  prev_spot_ = prev_spot;
210  }
211 
212  bool in_progress() {
213  return success_.load() == 0;
214  }
215 
216  bool result() {
217  assert(success_.load() != 0);
218  return success_.load() == 1;
219  }
220 
221  bool success() {
222  uint64_t temp = 0;
223  success_.compare_exchange_strong(temp, 1);
224  return temp == 0 || temp == 1;
225  }
226 
227  bool fail() {
228  uint64_t temp = 0;
229  success_.compare_exchange_strong(temp, 2);
230  return temp == 1;
231  }
232 
234  void * get_logical_value() {
235  uint64_t state = success_.load();
236  if (state == 1) {
237  return reinterpret_cast<void *>(val_);
238  } else {
239  return reinterpret_cast<void *>(Vector<T>::c_not_value_);
240  }
241  }
242 
244  void * complete(void *value, std::atomic<void *> *address) {
245  std::atomic<T> * spot = reinterpret_cast<std::atomic<T> *>(address);
246  T current_prev = prev_spot_->load();
247 
248 
250  while (in_progress() && progAssur.isDelayed()) {
251  if (current_prev == Vector<T>::c_not_value_) {
252  fail();
253  break;
254  } else if (vec_->internal_array.is_descriptor(current_prev, prev_spot_)) {
255  continue;
256  } else {
257  success();
258  break;
259  }
260  }
261 
262  bool op_res;
263  if (in_progress()) {
264  op_res = fail();
265  } else {
266  op_res = result();
267  }
268 
269  T new_current = reinterpret_cast<T>(util::memory::rc::mark_first(this));
270 
271  if (op_res) {
272  assert(success());
273  assert(success_.load() == 1);
274  if (spot->compare_exchange_strong(new_current, val_)) {
275  new_current = val_;
276  }
277  } else {
278  assert(!success());
279  assert(success_.load() == 2);
280  if (spot->compare_exchange_strong(new_current,
281  reinterpret_cast<T>(Vector<T>::c_not_value_))) {
282  new_current = reinterpret_cast<T>(Vector<T>::c_not_value_);
283  }
284  }
285 
286  return reinterpret_cast<void *>(new_current);
287  }
288 
291  }
292 
293 
294  private:
295  T val_;
296  Vector<T> *vec_;
297  std::atomic<T> * prev_spot_ {nullptr};
298  std::atomic<uint64_t> success_ {0};
299 };
300 
301 template<typename T>
302 class PushOpHelper: public tervel::util::Descriptor {
303  public:
304  explicit PushOpHelper(PushOp<T> *op)
305  : op_(op) {}
306 
307  void set_idx(uint64_t i) {
308  idx_ = i;
309  };
310 
311  uint64_t idx() {
312  return idx_;
313  };
314 
315  bool in_progress() {
316  return (success_.load() == 0);
317  };
318 
319  bool result() {
320  assert(success_.load() != 0);
321  if (success_.load() == 2) {
322  return false;
323  } else if (op_->helper_.load() == nullptr) {
324  return associate();
325  } else if (op_->helper_.load() == this) {
326  return true;
327  } else {
328  assert(op_->helper_.load() != nullptr);
329  return false;
330  }
331  };
332 
333  bool associate() {
334  assert(success_.load() == 1);
335  PushOpHelper *temp_null = nullptr;
336  bool res = op_->helper_.compare_exchange_strong(temp_null, this);
337  if (res || temp_null == this) {
338  assert(op_->helper_.load() == this);
339  return true;
340  } else {
341  assert(op_->helper_.load() != this);
342  return false;
343  }
344  };
345 
346  bool success() {
347  uint64_t temp = 0;
348  if ( (success_.compare_exchange_strong(temp, 1) || temp == 1)
349  && associate() ) {
350  assert(op_->helper_.load() == this);
351  assert(success_.load() == 1);
352  return true;
353  } else {
354  assert(success_.load() == 2);
355  return false;
356  }
357  };
358 
359  bool fail() {
360  uint64_t temp = 0;
361  if (success_.compare_exchange_strong(temp, 2) || temp == 2) {
362  return false;
363  } else {
364  assert(success_.load() == 1);
365  if (associate()) {
366  return true;
367  } else {
368  return false;
369  }
370  }
371  };
372 
374  void * complete(void *value, std::atomic<void *> *address) {
375  std::atomic<T> * spot = reinterpret_cast<std::atomic<T> *>(address);
376 
377  // Check if placed correctly.
378  if (idx_ == 0) {
379  success();
380  } else {
381  std::atomic<T> *spot_prev = op_->vec_->internal_array.get_spot(idx_-1);
382  T current_prev = spot_prev->load();
383 
384  while (in_progress() && op_->helper_.load() == nullptr) {
385  if (current_prev == Vector<T>::c_not_value_) {
386  fail();
387  } else if (op_->vec_->internal_array.is_descriptor(
388  current_prev, spot_prev)) {
389  continue;
390  } else {
391  success();
392  }
393  break;
394  } // while op not done
395  } // else not first position
396 
397  bool op_res;
398  if (in_progress()) {
399  op_res = fail();
400  } else {
401  op_res = result();
402  }
403 
404  T new_current = reinterpret_cast<T>(util::memory::rc::mark_first(this));
405  assert(reinterpret_cast<T>(value) == new_current);
406  if (op_res) {
407  if (spot->compare_exchange_strong(new_current, op_->new_val_)) {
408  new_current = op_->new_val_;
409  }
410  } else {
411  if (spot->compare_exchange_strong(new_current,
412  reinterpret_cast<T>(Vector<T>::c_not_value_))) {
413  new_current = reinterpret_cast<T>(Vector<T>::c_not_value_);
414  }
415  }
416 
417  return reinterpret_cast<void *>(new_current);
418  } // complete
419 
421  void * get_logical_value() {
422  if (in_progress()) {
423  return reinterpret_cast<void *>(Vector<T>::c_not_value_);
424  } else {
425  return reinterpret_cast<void *>(op_->new_val_);
426  }
427  } // get_logical_value
428 
438  bool on_watch(std::atomic<void *> *address, void * value) {
441  t_SlotID::SHORTUSE, op_, address, value);
442 
443  if (success) {
444  complete(value, address);
445  }
446  return false;
447  };
448 
449 
450  private:
451  PushOp<T> *op_;
452  uint64_t idx_;
453  std::atomic<uint64_t> success_ {0};
454 }; // class PushOpHelper
455 
456 } // namespace vector
457 } // namespace wf
458 } // namespace containers
459 } // namespace tervel
460 #endif // __TERVEL_CONTAINERS_WF_VECTOR_PUSH_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.
static size_t execute(Vector< T > *vec, T val)
Definition: pushback_op.h:63
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: pushback_op.h:244
__thread void * tl_control_word
uint64_t idx()
Definition: pushback_op.h:311
Definition: pushback_op.h:51
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 * get_logical_value()
This method is implemented by each sub class.
Definition: pushback_op.h:234
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
TODO(steven):
Definition: mcas.h:36
void set_prev_spot(std::atomic< T > *prev_spot)
Definition: pushback_op.h:208
PushOpHelper(PushOp< T > *op)
Definition: pushback_op.h:304
std::atomic< uint64_t > success_
Definition: pushback_op.h:453
bool in_progress()
Definition: pushback_op.h:212
bool associate()
Definition: pushback_op.h:333
~PushOp()
Definition: pushback_op.h:57
void set_idx(uint64_t i)
Definition: pushback_op.h:307
T new_val_
Definition: pushback_op.h:197
bool in_progress()
Definition: pushback_op.h:315
bool fail()
Definition: pushback_op.h:359
std::atomic< uint64_t > success_
Definition: pushback_op.h:298
void set_control_word()
Definition: pushback_op.h:289
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
Vector< T > * vec_
Definition: pushback_op.h:196
void help_complete()
Implementations of this function that upon its return the operation described in the OpRecord has bee...
Definition: pushback_op.h:136
This defines the Descriptor class, this class is designed to be extend and be used in conjunction wit...
Definition: descriptor.h:60
void * get_logical_value()
This method is implemented by each sub class.
Definition: pushback_op.h:421
T val_
Definition: pushback_op.h:295
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 ...
PushDescr(T val, Vector< T > *vec)
Definition: pushback_op.h:204
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
This class is used to create Operation Records.
Definition: progress_assurance.h:52
Definition: pushback_op.h:47
bool success()
Definition: pushback_op.h:346
uint64_t idx_
Definition: pushback_op.h:452
PushOp< T > * op_
Definition: pushback_op.h:447
virtual bool on_watch(std::atomic< void * > *, void *)
This method is optional to implement for each sub class.
Definition: descriptor.h:99
bool result()
Definition: pushback_op.h:216
Vector< T > * vec_
Definition: pushback_op.h:296
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: pushback_op.h:374
bool on_watch(std::atomic< void * > *address, void *value)
This method is optional to implement for each sub class.
Definition: pushback_op.h:438
PushOp(Vector< T > *vec, T val)
Definition: pushback_op.h:53
bool isDelayed(size_t val=1)
Definition: progress_assurance.h:125
uint64_t result()
Definition: pushback_op.h:130
bool result()
Definition: pushback_op.h:319
Definition: progress_assurance.h:120
bool fail()
Definition: pushback_op.h:227
bool is_watched()
Definition: pushback_op.h:184
std::atomic< T > * prev_spot_
Definition: pushback_op.h:297
bool success()
Definition: pushback_op.h:221
SlotID
Definition: hazard_pointer.h:58
std::atomic< PushOpHelper< T > * > helper_
Definition: pushback_op.h:198