Skip to content

Commit 8dd4f47

Browse files
committed
page 220
1 parent d0ebbb1 commit 8dd4f47

File tree

1 file changed

+60
-28
lines changed

1 file changed

+60
-28
lines changed

lfq_gen/lfq_gen.cpp

Lines changed: 60 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,41 @@ class lock_free_queue {
2222
// Keeping the structure within a machine word makes it more likely
2323
// that the atomic operations can be lock-free on many platforms
2424
unsigned internal_count:30;
25-
unsigned external_counters:2; // 2
25+
unsigned external_counters:2;
2626
};
2727

2828
struct node
2929
{
3030
std::atomic<T*> data;
31-
std::atomic<node_counter> count; // 3
32-
counted_node_ptr next;
31+
std::atomic<node_counter> count;
32+
std::atomic<counted_node_ptr> next; // 1
3333

3434
node() {
3535
node_counter new_count;
3636
new_count.internal_count = 0;
3737
// queue's own counters: only head and tail (max = 2)
38-
new_count.external_counters = 2; // 4
38+
new_count.external_counters = 2;
3939
count.store(new_count);
4040

4141
next.ptr = nullptr;
4242
next.external_count = 0;
4343
}
4444

4545
/**
46-
* Decreases this node's internal_count and deletes useless node */
46+
* Decreases this node's internal_count and deletes useless node.
47+
* Release the single reference held by this thread */
4748
void release_ref() {
4849
node_counter old_counter = count.load(std::memory_order_relaxed);
4950
node_counter new_counter;
5051

5152
do {
5253
new_counter = old_counter;
53-
--new_counter.internal_count; // 1
54-
} while (!count.compare_exchange_strong(old_counter, new_counter, // 2
54+
--new_counter.internal_count;
55+
} while (!count.compare_exchange_strong(old_counter, new_counter,
5556
std::memory_order_acquire, std::memory_order_relaxed));
5657

5758
if (!new_counter.internal_count && !new_counter.external_counters) {
58-
delete this; // 3
59+
delete this;
5960
}
6061
}
6162

@@ -94,20 +95,35 @@ class lock_free_queue {
9495
node_counter new_counter;
9596
do {
9697
new_counter = old_counter;
97-
--new_counter.external_counters; // 1
98-
new_counter.internal_count += count_increase; // 2
99-
} while (!ptr->count.compare_exchange_strong(old_counter, new_counter, // 3
98+
--new_counter.external_counters;
99+
new_counter.internal_count += count_increase;
100+
} while (!ptr->count.compare_exchange_strong(old_counter, new_counter,
100101
std::memory_order_acquire, std::memory_order_relaxed));
101102

102103
// delete node if all counters became 0
103104
if (!new_counter.internal_counter && !new_counter.external_counters) {
104-
delete ptr; // 4
105+
delete ptr;
105106
}
106107
};
108+
109+
void set_new_tail(counted_node_ptr& old_tail, counted_node_ptr const& new_tail) { // 1
110+
node* const current_tail_ptr = old_tail.ptr;
111+
112+
// if another thread updated tail -> check that new tail pointer to node is the same as
113+
// it was before loop -> break if not the same
114+
while (!tail.compare_exchange_weak(old_tail, new_tail) && old_tail.ptr == current_tail_ptr); // 2
115+
116+
if (old_tail.ptr == current_tail_ptr) // 3
117+
// ptr is the same once the loop has exited, successfully set the tail
118+
free_external_count(old_tail); // 4
119+
else
120+
// another thread will have freed the counter, release the single reference held by this thread
121+
current_tail_ptr->release_ref(); // 5
122+
}
107123
};
108124

109125
std::atomic<counted_node_ptr> head;
110-
std::atomic<counted_node_ptr> tail; // 1
126+
std::atomic<counted_node_ptr> tail;
111127

112128
public:
113129
/**
@@ -121,54 +137,70 @@ class lock_free_queue {
121137
counted_node_ptr old_tail = tail.load();
122138

123139
for (;;) {
124-
increase_external_count(tail, old_tail); // 5
140+
increase_external_count(tail, old_tail);
125141

126142
T* old_data = nullptr;
127-
// no other thread can modify tail while data is not nullptr
143+
128144
if (old_tail.ptr->data.compare_exchange_strong(old_data, new_data.get())) // 6
129145
{
130-
old_tail.ptr->next = new_next;
131-
old_tail = tail.exchange(new_next);
132-
// now node is fully pushed (data is nullptr), so other threads
133-
// can proceed with their pushes
134-
free_external_counter(old_tail); // 7
146+
// successfully set the tail's node's data pointer to 'new_data' because it was 'nullptr'
147+
counted_node_ptr old_next = { 0 };
148+
if (!old_tail.ptr->next.compare_exchange_strong(old_next, new_next)) { // 7
149+
// another thread has already helped this thread and set the next pointer in step 10
150+
delete new_next.ptr; // 8: don't need the new empty node, delete it
151+
new_next = old_next; // 9: use the next value that the other thread set for updating tail
152+
}
153+
set_new_tail(old_tail, new_next);
135154
new_data.release();
136155
break;
137156
}
138-
old_tail.ptr->release_ref(); //
157+
else { // 10: help the successful thread to complete the update: set 'next' and 'tail'
158+
// the tail's node's data pointer was not 'nullptr'
159+
counted_node_ptr old_next = { 0 };
160+
if (old_tail.ptr->next.compare_exchange_strong(old_next, new_next)) { // 11
161+
old_next = new_next; // 12
162+
new_next.ptr = new node; // 13
163+
}
164+
set_new_tail(old_tail, old_next); // 14
165+
}
139166
}
140167
}
141168

142169
/**
143170
* Pops node from the queue */
144171
std::unique_ptr<T> pop() {
145-
counted_node_ptr old_head = head.load(std::memory_order_relaxed); // 1
172+
counted_node_ptr old_head = head.load(std::memory_order_relaxed);
146173

147174
for (;;) {
148-
increase_external_count(head, old_head); // 2
175+
increase_external_count(head, old_head);
149176
node* const ptr = old_head.ptr;
150177

151-
// if queue is empty ('ptr' is null)
178+
/* if head's counted_node_ptr's ptr points to the same node as tail
179+
// it means that the queue is empty so return new empty unique_ptr */
152180
if (ptr == tail.load().ptr) {
153-
prt->release_ref(); // 3
181+
prt->release_ref();
154182
return std::unique_ptr<T>();
155183
}
156184

185+
counted_node_ptr next = ptr->next.load(); // 2
157186
// this compares the external count and pointer as a single entity
158-
if (head.compare_exchange_strong(old_head, ptr->next)) { // 4
187+
if (head.compare_exchange_strong(old_head, next)) {
159188
T* const res = ptr->data.exchange(nullptr);
160-
free_external_count(old_head); // 5
189+
free_external_count(old_head);
161190
return std::unique_ptr<T>(res);
162191
}
163192

164-
ptr->release_ref; // 6
193+
ptr->release_ref;
165194
}
166195
}
167196
};
168197

169198

170199
int main()
171200
{
201+
lock_free_queue<int>* lfq = new lock_free_queue<int>();
202+
lfq->push(5);
203+
172204
return 0;
173205
}
174206

0 commit comments

Comments
 (0)