Tervel  1.0.0
A collection of wait-free containers and algorithms.
mcas_imp.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_MCAS_MCAS_IMP_H_
26 #define TERVEL_MCAS_MCAS_IMP_H_
27 
28 
29 namespace tervel {
30 namespace algorithms {
31 namespace wf {
32 namespace mcas {
33 
34 template<class T>
35 bool MultiWordCompareAndSwap<T>::add_cas_triple(std::atomic<T> *address, T expected_value,
36  T new_value) {
37  //First check to make sure the values do not have any reserved bits
38  if (!tervel::util::isValid(reinterpret_cast<void *>(expected_value)) ||
39  !tervel::util::isValid(reinterpret_cast<void *>(new_value))) {
40  return false;
41  } else if (row_count_ == max_rows_) {
42  return false;
43  } else {
44  cas_rows_[row_count_].address_ = address;
45  cas_rows_[row_count_].expected_value_ = expected_value;
46  cas_rows_[row_count_].new_value_ = new_value;
47  cas_rows_[row_count_++].helper_.store(nullptr);
48 
49  //Sort them
50  // Probally can be done more efficent
51  for (int i = (row_count_ - 1); i > 0; i--) {
52  if (cas_rows_[i] > cas_rows_[i-1]) {
53  swap(cas_rows_[i], cas_rows_[i-1]);
54  } else if (cas_rows_[i] == cas_rows_[i-1]) {
55  for (; i < row_count_-1; i++) {
56  swap(cas_rows_[i], cas_rows_[i+1]);
57  }
58  row_count_--;
59 
60  /* We remove the row because the address already exists */
61  cas_rows_[row_count_].address_ = nullptr;
62  cas_rows_[row_count_].expected_value_ = nullptr;
63  cas_rows_[row_count_].new_value_ = nullptr;
64  return false;
65  }
66  }
67  return true;
68  }
69 }
70 
71 template<class T>
74  bool res = mcas_complete(0);
75  cleanup(res);
76  return res;
77 }
78 
79 template<class T>
81  int start_pos = 1;
83  assert(cas_rows_[0].helper_.load() != nullptr);
84  assert(current_row->helper_.load() != nullptr);
85  start_pos = current_row - &(cas_rows_[0]);
86  return mcas_complete(start_pos, false);
87 }
88 
89 template<class T>
90 bool MultiWordCompareAndSwap<T>::mcas_complete(int start_pos, bool wfmode) {
95  for (int pos = start_pos; pos < row_count_; pos++) {
97 
98  CasRow<T> * row = &(cas_rows_[pos]);
99 
100  assert(pos == 0 || cas_rows_[pos-1].helper_.load());
101 
102  /* Read the current value of the address */
103  T current_value = row->address_->load();
104 
105  while (row->helper_.load() == nullptr) {
106  /* Loop until this row's helper is no longer null */
107 
108  if (state_.load() != MCasState::IN_PROGRESS) {
109  /* Checks if the operation has been completed */
110  return (state_.load() == MCasState::PASS);
111  } else if (!wfmode && progAssur.isDelayed()) {
112  /* Check if we need to enter wf_mode */
114  /* If this is our operation then make an annoucnement */
116  assert(state_.load() != MCasState::IN_PROGRESS);
117  return (state_.load() == MCasState::PASS);
118  } else {
119  /* Otherwise perform a recursive return */
121  return false;
122  }
123  }
124 
125  /* Process the current value at the address */
126  /* Now Check if the current value is descriptor */
128  reinterpret_cast<void *>(current_value))) {
129  /* Remove it by completing the op, try again */
130  current_value = this->mcas_remove(pos, current_value);
131 
132  /* Check if we are executing a recursive return and if so determine
133  * if we are at our own operation or need to return farther. */
136  /* we are back to our own operation so re-read process the
137  * current value */
139  current_value = row->address_->load();
140  } else {
141  /* we need to return some more */
142  return false;
143  }
144  }
145  } else if (current_value != row->expected_value_) {
146  /* Current value does not match the expected value and it is a non
147  * descriptor type, the mcas operation should fail. */
148  Helper<T>* temp_null = nullptr;
149  /* First try to disable row by assigning a failed constant */
150  if (row->helper_.compare_exchange_strong(temp_null,
151  reinterpret_cast<Helper<T> *>(MCAS_FAIL_CONST))
152  || temp_null == reinterpret_cast<Helper<T> *>(MCAS_FAIL_CONST)) {
153  /* if row was disabled then set the state to FAILED */
154  MCasState temp_state = MCasState::IN_PROGRESS;
155  this->state_.compare_exchange_strong(temp_state, MCasState::FAIL);
156  assert(this->state_.load() == MCasState::FAIL);
157  return false;
158  } else {
159  /* the row was associated, lets re-evaluate the current state */
160  continue;
161  }
162  } else {
163  /* Else the current_value matches the expected_value_ */
165  Helper<T> >(this, row);
166  if (row->address_->compare_exchange_strong(current_value,
167  reinterpret_cast<T>(util::memory::rc::mark_first(helper)))) {
168  /* helper was successfully placed at the address */
169  Helper<T> * temp_null = nullptr;
170  if (row->helper_.compare_exchange_strong(temp_null, helper)
171  || temp_null == helper) {
172  /* We successfully associated the helper the row */
173  break; /* break the while loop, on to the next row! */
174  } else {
175  /* We failed to associate the helper, remove it, this implies that
176  * the operation is over
177  */
178  T temp_helper = reinterpret_cast<T>(
180 
181  row->address_->compare_exchange_strong(temp_helper,
182  row->expected_value_);
183 
185 
186  if(row->helper_.load() == reinterpret_cast<Helper<T> *>(
187  MCAS_FAIL_CONST)) {
188  MCasState temp_state = MCasState::IN_PROGRESS;
189  this->state_.compare_exchange_strong(temp_state, MCasState::FAIL);
190  }
191  assert(row->helper_.load());
192  assert(state_.load() != MCasState::IN_PROGRESS);
193  return (state_.load() == MCasState::PASS);
194  }
195  } else {
196  /* We failed to place helper, re-evaluate the current_value
197  * Set no check to true since it was never used.
198  */
199  util::memory::rc::free_descriptor(helper, true);
200  continue;
201  }
202  } // End Else Try to replace
203  } // End While Current helper is null
204 
205  if (row->helper_.load() == reinterpret_cast<Helper<T> *>(MCAS_FAIL_CONST)) {
206  MCasState temp_state = MCasState::IN_PROGRESS;
207  this->state_.compare_exchange_strong(temp_state, MCasState::FAIL);
208  assert(this->state_.load() == MCasState::FAIL);
209  return false;
210  }
211  } // End For Loop on CasRows
212 
213  /* All rows have been associated, so set the state to passed! */
214  MCasState temp_state = MCasState::IN_PROGRESS;
215  if (this->state_.compare_exchange_strong(temp_state, MCasState::PASS)) {
216  temp_state = MCasState::PASS;
217  }
218  assert(this->state_.load() != MCasState::IN_PROGRESS);
219  return (temp_state == MCasState::PASS);
220 } // End Complete function.
221 
222 template<class T>
223 T MultiWordCompareAndSwap<T>::mcas_remove(const int pos, T value) {
224  std::atomic<void *> *address = reinterpret_cast<std::atomic<void *>*>(
225  cas_rows_[pos].address_);
227  reinterpret_cast<void *>(value));
228 
229  // First get a watch on the object.
230  bool watched = util::memory::rc::watch(descr, address,
231  reinterpret_cast<void *>(value));
232 
233  if (watched) {
234  // Now unwatch it, crazy right? But there is a reason...
236 
237  /* If we watched it and it is a MCH for this operation, then act of
238  * watching will call the MCH's on_watch function, which will associate it
239  * with this cas row...thus if cas_rows_[pos].helper != nullprt, then this
240  * could have been a MCH to for this row but it does not matter, because
241  * this row is already done, so we need to go to the next row.
242  */
243  if (this->cas_rows_[pos].helper_.load() != nullptr) {
244  return static_cast<T>(nullptr); // Does not matter, it wont be used.
245  } else {
246  /* It is some other threads operation, so lets complete it.*/
247  util::memory::rc::remove_descriptor(value, address);
248  }
249  }
250  // watch failed do to the value at the address changing, return new value
251  return reinterpret_cast<T>(address->load());
252 }
253 
254 template<class T>
256  for (int pos = 0; pos < row_count_; pos++) {
257  /* Loop for each row in the op*/
258  CasRow<T> * row = &cas_rows_[pos];
259 
260  assert(row->helper_.load() != nullptr);
261 
262  Helper<T> * temp_helper = row->helper_.load();
263  T marked_helper = reinterpret_cast<T>(
264  util::memory::rc::mark_first(temp_helper));
265  if (temp_helper == reinterpret_cast<Helper<T> *>(MCAS_FAIL_CONST)) {
266  // There can not be any any associated rows beyond this position.
267  return;
268  } else {
269  // else there was a helper placed for this row
270  T cur_value = row->address_->load();
271  if (cur_value == marked_helper) {
272  if (success) {
273  row->address_->compare_exchange_strong(cur_value, row->new_value_);
274  } else {
275  row->address_->compare_exchange_strong(cur_value,
276  row->expected_value_);
277  }
278  } // End If the current value matches the helper placed for this op
279  } // End Else there was a helper placed for this row
280  } // End While loop over casrows.
281 } // End cleanup function.
282 
283 } // namespace mcas
284 } // namespace wf
285 } // namespace algorithms
286 } // namespace tervel
287 
288 #endif // TERVEL_MCAS_MCAS_IMP_H_
DescrType * get_descriptor(Args &&...args)
Constructs and returns a descriptor.
Definition: descriptor_util.h:50
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
T mcas_remove(const int pos, T value)
This function insures that upon its return that *(cas_rows_[pos].address) no longer equals value...
Definition: mcas_imp.h:223
bool isValid(void *value)
Returns whether or not the passed value is has one of the reserved bits set to 1. ...
Definition: util.h:155
static bool is_watched(Element *descr, HazardPointer *const hazard_pointer=tervel::tl_thread_info->get_hazard_pointer())
This method is used to determine if a hazard pointer watch exists on a passed value.
This class is used to represent a one of the M CAS operations performed by an MCAS operation...
Definition: mcas_casrow.h:45
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
T expected_value_
Definition: mcas_casrow.h:85
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 unwatch(tervel::util::Descriptor *descr)
This method is used to decrement the reference count of the passed descriptor object.
Definition: descriptor_util.h:139
bool add_cas_triple(std::atomic< T > *address, T expected_value, T new_value)
This function is used to add a CAS triple to the MCAS operation.
Definition: mcas_imp.h:35
MCasState
This enum is used to indicate the state of an mcas operation DELETED is used in debugging procedures...
Definition: mcas.h:143
bool execute()
This function is called after all the CAS triples have been added to the operation.
Definition: mcas_imp.h:72
std::atomic< T > * address_
Definition: mcas_casrow.h:82
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
std::atomic< Helper< T > * > helper_
Definition: mcas_casrow.h:87
static bool recursive_return(bool change=false, bool value=false)
bool mcas_complete(int start_pos, bool wfmode=false)
This function is used to complete a currently executing MCAS operation It is most likely that this op...
Definition: mcas_imp.h:90
static size_t recursive_depth(size_t i=0)
Adds the passed value and returns the pre-incremented value.
void cleanup(bool success)
This function is used to cleanup a completed MCAS operation It removes each MCH placed during this op...
Definition: mcas_imp.h:255
This defines the Descriptor class, this class is designed to be extend and be used in conjunction wit...
Definition: descriptor.h:60
tervel::util::Descriptor * unmark_first(void *descr)
This returns an unbitmarked reference.
Definition: descriptor_util.h:178
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
bool is_descriptor_first(void *descr)
This returns whether or not the least significant bit holds a bitmark.
Definition: descriptor_util.h:189
T new_value_
Definition: mcas_casrow.h:86
static void set_recursive_return()
Definition: recursive_action.h:57
This class is the MCAS operation's helper.
Definition: mcas_casrow.h:37
void * remove_descriptor(void *expected, std::atomic< void * > *address)
This method is used to remove a descriptor object that is conflict with another threads operation...
Definition: descriptor_util.h:208
bool isDelayed(size_t val=1)
Definition: progress_assurance.h:125
static void clear_recursive_return()
Definition: recursive_action.h:61
Definition: progress_assurance.h:120