Skip to content

Commit 8944221

Browse files
authored
Refactored queue_tm a bit (#24)
1 parent 7b4d9f0 commit 8944221

File tree

3 files changed

+124
-89
lines changed

3 files changed

+124
-89
lines changed

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,17 @@ For example, a transactional dynamic queue data structure could be defined with
189189
the help of `std::shared_ptr` as
190190

191191
```c++
192-
template <class Value> struct queue_tm {
193-
using value_t = Value;
194-
// ...
195-
private:
192+
template <class Value> class queue_tm {
196193
struct node_t {
197194
atom<std::shared_ptr<node_t>> m_next;
198-
value_t m_value;
199-
node_t(const value_t &value) : m_value(value) {}
195+
Value m_value;
196+
node_t(const Value &value) : m_value(value) {}
200197
};
201198

202199
atom<std::shared_ptr<node_t>> m_first;
203200
atom<std::shared_ptr<node_t>> m_last;
201+
202+
// ...
204203
};
205204
```
206205

internals/include/testing/queue_tm.hpp

Lines changed: 117 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -11,99 +11,133 @@
1111
namespace testing {
1212

1313
/// A transactional queue for testing purposes.
14-
template <class Value> struct queue_tm {
14+
template <class Value> class queue_tm {
15+
struct node_t {
16+
trade::atom<std::shared_ptr<node_t>> m_next;
17+
Value m_value;
18+
#ifndef NDEBUG
19+
~node_t() { --s_live_nodes; }
20+
#endif
21+
template <class ForwardableValue>
22+
node_t(ForwardableValue &&value)
23+
: m_value(std::forward<ForwardableValue>(value)) {
24+
#ifndef NDEBUG
25+
++s_live_nodes;
26+
#endif
27+
}
28+
};
29+
30+
trade::atom<std::shared_ptr<node_t>> m_first;
31+
trade::atom<std::shared_ptr<node_t>> m_last;
32+
33+
public:
1534
using value_t = Value;
1635

1736
queue_tm() = default;
1837

1938
queue_tm(const queue_tm &) = delete;
2039
queue_tm &operator=(const queue_tm &) = delete;
2140

22-
size_t size() {
23-
return trade::atomically(trade::assume_readonly, [&]() {
24-
size_t n = 0;
25-
for (auto node = m_first.load(); node; node = node->m_next)
26-
n += 1;
27-
return n;
28-
});
29-
}
30-
31-
operator std::vector<value_t>() const {
32-
std::vector<value_t> values;
33-
trade::atomically(trade::assume_readonly, [&]() {
34-
values.clear();
35-
for (auto node = m_first.load(); node; node = node->m_next)
36-
values.push_back(node->m_value);
37-
});
38-
return values;
39-
}
40-
41-
void clear() {
42-
trade::atomically([&]() { m_first = m_last = nullptr; });
43-
}
44-
45-
void push_back(const value_t &value) {
46-
std::shared_ptr<node_t> node(new node_t{value});
47-
trade::atomically([&]() {
48-
if (auto prev = m_last.load())
49-
prev->m_next = m_last = node;
50-
else
51-
m_first = m_last = node;
52-
});
53-
}
54-
55-
void push_front(const value_t &value) {
56-
std::shared_ptr<node_t> node(new node_t{value});
57-
trade::atomically([&]() {
58-
if (auto next = m_first.load()) {
59-
m_first = node;
60-
node->m_next = next;
61-
} else {
62-
m_last = m_first = node;
63-
}
64-
});
65-
}
66-
67-
std::optional<value_t> try_pop_front() {
68-
return trade::atomically([&]() -> std::optional<value_t> {
69-
if (auto first = m_first.load()) {
70-
if (auto next = first->m_next.load())
71-
m_first = next;
72-
else
73-
m_last = m_first = nullptr;
74-
return first->m_value;
75-
}
76-
return std::nullopt;
77-
});
78-
}
79-
80-
bool empty() {
81-
return trade::atomically(trade::assume_readonly,
82-
[&]() { return !m_first.load(); });
83-
}
84-
85-
value_t pop_front() {
86-
return trade::atomically([&]() {
87-
if (auto opt_value = try_pop_front())
88-
return opt_value.value();
89-
trade::retry();
90-
});
91-
}
92-
93-
static std::atomic<size_t> s_live_nodes; // Only for testing purposes
94-
95-
private:
96-
struct node_t {
97-
trade::atom<std::shared_ptr<node_t>> m_next;
98-
value_t m_value;
99-
~node_t() { --s_live_nodes; }
100-
node_t(const value_t &value) : m_value(value) { ++s_live_nodes; }
101-
};
41+
size_t size() const;
10242

103-
trade::atom<std::shared_ptr<node_t>> m_first;
104-
trade::atom<std::shared_ptr<node_t>> m_last;
43+
bool empty() const;
44+
45+
void clear();
46+
47+
template <class ForwardableValue> void push_back(ForwardableValue &&value);
48+
49+
template <class ForwardableValue> void push_front(ForwardableValue &&value);
50+
51+
std::optional<Value> try_pop_front();
52+
53+
Value pop_front();
54+
55+
operator std::vector<Value>() const;
56+
57+
#ifndef NDEBUG
58+
static std::atomic<size_t> s_live_nodes;
59+
#endif
10560
};
10661

62+
template <class Value> size_t queue_tm<Value>::size() const {
63+
return trade::atomically(trade::assume_readonly, [&]() {
64+
size_t n = 0;
65+
for (auto node = m_first.load(); node; node = node->m_next)
66+
n += 1;
67+
return n;
68+
});
69+
}
70+
71+
template <class Value> bool queue_tm<Value>::empty() const {
72+
return trade::atomically(trade::assume_readonly,
73+
[&]() { return !m_last.load(); });
74+
}
75+
76+
template <class Value> void queue_tm<Value>::clear() {
77+
trade::atomically([&]() { m_first = m_last = nullptr; });
78+
}
79+
80+
template <class Value>
81+
template <class ForwardableValue>
82+
void queue_tm<Value>::push_back(ForwardableValue &&value) {
83+
std::shared_ptr<node_t> node(
84+
new node_t(std::forward<ForwardableValue>(value)));
85+
trade::atomically([&]() {
86+
if (auto prev = m_last.load())
87+
prev->m_next = m_last = node;
88+
else
89+
m_first = m_last = node;
90+
});
91+
}
92+
93+
template <class Value>
94+
template <class ForwardableValue>
95+
void queue_tm<Value>::push_front(ForwardableValue &&value) {
96+
std::shared_ptr<node_t> node(
97+
new node_t(std::forward<ForwardableValue>(value)));
98+
trade::atomically([&]() {
99+
if (auto next = m_first.load()) {
100+
m_first = node;
101+
node->m_next = next;
102+
} else {
103+
m_last = m_first = node;
104+
}
105+
});
106+
}
107+
108+
template <class Value> std::optional<Value> queue_tm<Value>::try_pop_front() {
109+
return trade::atomically([&]() -> std::optional<Value> {
110+
if (auto first = m_first.load()) {
111+
if (auto next = first->m_next.load())
112+
m_first = next;
113+
else
114+
m_last = m_first = nullptr;
115+
return first->m_value;
116+
}
117+
return std::nullopt;
118+
});
119+
}
120+
121+
template <class Value> Value queue_tm<Value>::pop_front() {
122+
return trade::atomically([&]() {
123+
if (auto opt_value = try_pop_front())
124+
return opt_value.value();
125+
trade::retry();
126+
});
127+
}
128+
129+
template <class Value> queue_tm<Value>::operator std::vector<Value>() const {
130+
std::vector<Value> values;
131+
trade::atomically(trade::assume_readonly, [&]() {
132+
values.clear();
133+
for (auto node = m_first.load(); node; node = node->m_next)
134+
values.push_back(node->m_value);
135+
});
136+
return values;
137+
}
138+
139+
#ifndef NDEBUG
107140
template <class Value> std::atomic<size_t> queue_tm<Value>::s_live_nodes = 0;
141+
#endif
108142

109143
} // namespace testing

internals/testing/dynamic_test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,7 @@ auto dynamic_test = test([]() {
120120
verify(0 == q.size());
121121
}
122122

123+
#ifndef NDEBUG
123124
verify(!queue_tm<int>::s_live_nodes);
125+
#endif
124126
});

0 commit comments

Comments
 (0)