Tervel  1.0.0
A collection of wait-free containers and algorithms.
dequeue_op_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 
26 #ifndef TERVEL_CONTAINERS_WF_RINGBUFFER_RINGBUFFER_DEQUEUEOP_IMP_H_
27 #define TERVEL_CONTAINERS_WF_RINGBUFFER_RINGBUFFER_DEQUEUEOP_IMP_H_
28 
30 
31 namespace tervel {
32 namespace containers {
33 namespace wf {
34 
35 template<typename T>
36 void
39  int64_t head = this->rb_->getHead();
40  while(this->BufferOp::notDone()) {
41  if (this->rb_->isEmpty(this->rb_->getTail(), head)) {
42  this->fail();
43  return;
44  }
45  // assert(head <= this->rb_->getHead()); TODO(steven): is this right?
46 
47  int64_t seqid = head++;
48  uint64_t pos = this->rb_->getPos(seqid);
49  uintptr_t val;
50 
51  while(this->BufferOp::notDone()) {
52 
53  if (!this->rb_->readValue(pos, val)) {
54  continue;
55  }
56 
57  int64_t val_seqid;
58  bool val_isValueType;
59  bool val_isDelayedMarked;
60  this->rb_->getInfo(val, val_seqid, val_isValueType, val_isDelayedMarked);
61 
62 
63  if (val_seqid > head) {
64  // So check the next position
65  break;
66  } else if (val_isValueType) {
67  // it is a valueType, with seqid <= to the one we are working with...
68  // so we take it or try to any way...
69  // NOTE(steven): This may break FIFO, we may want to add a lower limit check.
70 
71  Helper * helper = new Helper(this, val);
72  uintptr_t helper_int = Helper::HelperType(helper);
73 
74  bool res = this->rb_->array_[pos].compare_exchange_strong(val, helper_int);
75  if (res) {
76  // Success!
77  // The following line is not hacky if you ignore the function name...
78  // it associates and then removes the object.
79  // It is also called by the memory protection watch function...
80  std::atomic<void *> *temp1;
81  temp1 = reinterpret_cast<std::atomic<void *> *>(&(this->rb_->array_[pos]));
82  void *temp2 = reinterpret_cast<void *>(helper_int);
83 
84  helper->on_watch(temp1, temp2);
85  if (!helper->valid()) {
86  helper->safe_delete();
87  }
88 
89 
90  // Op is Done!
91  return;
92  } else {
93  // Failure :(
94  delete helper;
95  continue; // re-examine position on the next loop
96  }
97 
98  } else {
99  // Its an EmptyType with a seqid <= to the one we are working with
100  // so lets mess shit up and set it delayed mark that will show them...
101  // but it is the simplest way to ensure nothing gets enqueued at this pos
102  // which allows us to keep fifo.
103  // If something did get enqueued then, it will be marked and we will check
104  // it on the next round
105  if (val_isDelayedMarked) {
106  // its already been marked, so move on to the next pos;
107  break;
108  } else {
109  this->rb_->atomic_delay_mark(pos);
110  continue; // re-read/process the value
111  }
112  } // its an EmptyType'
113  } // while notDone
114  } // while notDone
115 } // void RingBuffer<T>::DequeueOp::help_complete()
116 
117 template<typename T>
118 void *
121  bool res = BufferOp::privAssociate(h);
122  int64_t seqid = -1;
123  uintptr_t new_val;
124  uintptr_t old_val = h->old_value_;
125  if (res) {
126  seqid = RingBuffer<T>::getValueTypeSeqId(old_val);
127  int64_t next_seqid = this->rb_->nextSeqId(seqid);
128  new_val = RingBuffer<T>::EmptyType(next_seqid);
129  if (RingBuffer<T>::isDelayedMarked(old_val)) {
130  new_val = RingBuffer<T>::DelayMarkValue(new_val);
131  }
132  } else {
133  new_val = old_val;
134  Helper *htemp = this->helper_.load();
135  if (htemp != BufferOp::fail_val_) {
137  }
138  }
139  // Now we need to ensure the sequence counter does not false report full
140  // This is safe, because we did not observed a ValueType with a seqid
141  // < the one we dequeued and ensured no ValueType can be enqueued
142  // with a seqid < then the one we took? because of the atomic
143  // bitmark.
144  if (seqid != -1) {
145  int64_t head_temp = this->rb_->getHead();
146  while ( head_temp < (seqid + 1) ) {
147  if (this->rb_->casHead(head_temp, seqid + 1))
148  break;
149  }
150  }
151 
152  return reinterpret_cast<void *>(new_val);
153 }
154 
155 template<typename T>
156 bool
158 result(T &val) {
159  Helper * h;
160  if (BufferOp::isFail(h)) {
161  return false;
162  } else {
163  uintptr_t temp = h->old_value_;
164  val = getValueType(temp);
165  return true;
166  }
167 };
168 
169 } // namespace wf
170 } // namespace containers
171 } // namespace tervel
172 
173 #endif // TERVEL_CONTAINERS_WF_RINGBUFFER_RINGBUFFER_DEQUEUEOP_IMP_H_
void help_complete()
Implementations of this function that upon its return the operation described in the OpRecord has bee...
Definition: dequeue_op_imp.h:38
void * associate(Helper *h)
Definition: dequeue_op_imp.h:120
static uintptr_t EmptyType(int64_t seqid)
Creates a uintptr_t that represents an EmptyType.
Definition: ring_buffer_imp.h:358
bool privAssociate(Helper *h)
Definition: ring_buffer_op.h:59
void fail()
Definition: ring_buffer_op.h:73
TODO(steven):
Definition: mcas.h:36
const uintptr_t old_value_
Definition: helper.h:62
static int64_t getEmptyTypeSeqId(uintptr_t val)
Returns the seqid encoded in the passed value.
Definition: ring_buffer_imp.h:380
static constexpr Helper * fail_val_
Definition: ring_buffer_op.h:95
static uintptr_t DelayMarkValue(uintptr_t val)
Takes a uintptr_t and places a bitmark on the delayMark_lsb.
Definition: ring_buffer_imp.h:374
This is a non-blocking FIFO ring buffer design that was made wait-free by applying a progress assuran...
Definition: ring_buffer.h:73
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 result(T &val)
Definition: dequeue_op_imp.h:158
static T getValueType(uintptr_t val)
Returns the value type from a uintptr.
Definition: ring_buffer_imp.h:109
static int64_t getValueTypeSeqId(uintptr_t val)
Returns the seqid of the passed value.
Definition: ring_buffer_imp.h:385
RingBuffer< T > * rb_
Definition: ring_buffer_op.h:97
bool on_watch(std::atomic< void * > *address, void *expected)
This function is used to achieve a strong watch on an Element.
Definition: helper_imp.h:40
static uintptr_t HelperType(Helper *h)
Returns a uintptr_t for the passed helper object.
Definition: helper_imp.h:88
bool valid()
Definition: helper_imp.h:81
bool isFail(Helper *&h)
Definition: ring_buffer_op.h:78
bool notDone()
Definition: ring_buffer_op.h:87