Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/hotspot/share/jfr/utilities/jfrConcurrentLinkedListHost.inline.hpp
41149 views
1
/*
2
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*
23
*/
24
25
#ifndef SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP
26
#define SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP
27
28
#include "jfr/utilities/jfrConcurrentLinkedListHost.hpp"
29
30
#include "jfr/utilities/jfrRelation.hpp"
31
#include "jfr/utilities/jfrTypes.hpp"
32
#include "runtime/atomic.hpp"
33
#include "utilities/globalDefinitions.hpp"
34
35
/*
36
* The removal marker (i.e. the excision bit) is represented by '( )' as part of state description comments:
37
* "node --> next" becomes "(node) --> next", when node is logically deleted.
38
*/
39
template <typename Node>
40
inline Node* mark_for_removal(Node* node) {
41
assert(node != NULL, "invariant");
42
const Node* next = node->_next;
43
assert(next != NULL, "invariant");
44
Node* const unmasked_next = unmask(next);
45
return next == unmasked_next && cas(&node->_next, unmasked_next, set_excision_bit(unmasked_next)) ? unmasked_next : NULL;
46
}
47
48
/*
49
* The insertion marker (i.e. the insertion bit) is represented by '[ ]' as part of state description comments:
50
* "node --> next" becomes "[node] --> next", in an attempt to convey the node as exlusively reserved.
51
*/
52
template <typename Node>
53
inline bool mark_for_insertion(Node* node, const Node* tail) {
54
assert(node != NULL, "invariant");
55
return node->_next == tail && cas(&node->_next, const_cast<Node*>(tail), set_insertion_bit(tail));
56
}
57
58
/*
59
* Find a predecessor and successor node pair where successor covers predecessor (adjacency).
60
*/
61
template <typename Node, typename VersionHandle, template <typename> class SearchPolicy>
62
Node* find_adjacent(Node* head, const Node* tail, Node** predecessor, VersionHandle& version_handle, SearchPolicy<Node>& predicate) {
63
assert(head != NULL, "invariant");
64
assert(tail != NULL, "invariant");
65
assert(head != tail, "invariant");
66
Node* predecessor_next = NULL;
67
while (true) {
68
Node* current = head;
69
version_handle->checkout();
70
Node* next = Atomic::load_acquire(&current->_next);
71
do {
72
assert(next != NULL, "invariant");
73
Node* const unmasked_next = unmask(next);
74
// 1A: Locate the first node to keep as predecessor.
75
if (!is_marked_for_removal(next)) {
76
*predecessor = current;
77
predecessor_next = unmasked_next;
78
}
79
// 1B: Locate the next node to keep as successor.
80
current = unmasked_next;
81
if (current == tail) break;
82
next = current->_next;
83
} while (predicate(current, next));
84
// current represents the successor node from here on out.
85
// 2: Check predecessor and successor node pair for adjacency.
86
if (predecessor_next == current) {
87
// Invariant: predecessor --> successor
88
return current;
89
}
90
// 3: Successor does not (yet) cover predecessor.
91
// Invariant: predecessor --> (logically excised nodes) --> successor
92
// Physically excise one or more logically excised nodes in-between.
93
if (cas(&(*predecessor)->_next, predecessor_next, current)) {
94
// Invariant: predecessor --> successor
95
return current;
96
}
97
}
98
}
99
100
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
101
JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::JfrConcurrentLinkedListHost(Client* client) : _client(client) {}
102
103
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
104
bool JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::initialize() {
105
return true;
106
}
107
108
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
109
void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::insert_head(typename Client::Node* node,
110
typename Client::Node* head,
111
const typename Client::Node* tail) const {
112
Node* predecessor;
113
Node* successor;
114
HeadNode<Node> predicate(node);
115
VersionHandle version_handle = _client->get_version_handle();
116
while (true) {
117
// Find an adjacent predecessor and successor node pair.
118
successor = find_adjacent<Node, VersionHandle, HeadNode>(head, tail, &predecessor, version_handle, predicate);
119
// Invariant (adjacency): predecessor --> successor
120
// Invariant (optional: key-based total order): predecessor->key() < key && key <= successor->key().
121
// We can now attempt to insert the new node in-between.
122
node->_next = successor;
123
if (cas(&predecessor->_next, successor, node)) {
124
// Invariant: predecessor --> node --> successor
125
// An insert to head is a benign modification and will not need to be committed to the version control system.
126
return;
127
}
128
}
129
}
130
131
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
132
void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::insert_tail(typename Client::Node* node,
133
typename Client::Node* head,
134
typename Client::Node* last,
135
const typename Client::Node* tail) const {
136
assert(node != NULL, "invariant");
137
assert(head != NULL, "invariant");
138
assert(last != NULL, "invarinat");
139
assert(tail != NULL, "invariant");
140
// Mark the new node to be inserted with the insertion marker already.
141
node->_next = set_insertion_bit(const_cast<NodePtr>(tail));
142
// Invariant: [node]--> tail
143
assert(is_marked_for_insertion(node->_next), "invariant");
144
NodePtr predecessor;
145
LastNode<Node> predicate;
146
VersionHandle version_handle = _client->get_version_handle();
147
while (true) {
148
// Find an adjacent predecessor and successor node pair, where the successor == tail
149
const NodePtr successor = find_adjacent<Node, VersionHandle, LastNode>(last, tail, &predecessor, version_handle, predicate);
150
assert(successor == tail, "invariant");
151
// Invariant: predecessor --> successor
152
// We first attempt to mark the predecessor node to signal our intent of performing an insertion.
153
if (mark_for_insertion(predecessor, tail)) {
154
break;
155
}
156
}
157
// Predecessor node is claimed for insertion.
158
// Invariant: [predecessor] --> tail
159
assert(is_marked_for_insertion(predecessor->_next), "invariant");
160
assert(predecessor != head, "invariant");
161
if (Atomic::load_acquire(&last->_next) == predecessor) {
162
/* Even after we store the new node into the last->_next field, there is no race
163
because it is also marked with the insertion bit. */
164
last->_next = node;
165
// Invariant: last --> [node] --> tail
166
OrderAccess::storestore();
167
// Perform the link with the predecessor node, which by this store becomes visible for removal.
168
predecessor->_next = node;
169
// Invariant: predecessor --> [node] --> tail
170
} else {
171
assert(last == predecessor, "invariant");
172
last->_next = node;
173
// Invariant: last --> [node] --> tail
174
OrderAccess::storestore();
175
/* This implies the list is logically empty from the removal perspective.
176
cas is not needed here because inserts must not come in from the head side
177
concurrently with inserts from tail which are currently blocked by us.
178
Invariant (logical): head --> tail. */
179
head->_next = node;
180
// Invariant: head --> [node] --> tail
181
}
182
OrderAccess::storestore();
183
// Publish the inserted node by removing the insertion marker.
184
node->_next = const_cast<NodePtr>(tail);
185
// Invariant: last --> node --> tail (possibly also head --> node --> tail)
186
}
187
188
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
189
typename Client::Node* JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::remove(typename Client::Node* head,
190
const typename Client::Node* tail,
191
typename Client::Node* last /* NULL */,
192
bool insert_is_head /* true */) {
193
assert(head != NULL, "invariant");
194
assert(tail != NULL, "invariant");
195
assert(head != tail, "invariant");
196
NodePtr predecessor;
197
NodePtr successor;
198
NodePtr successor_next;
199
SearchPolicy<Node> predicate;
200
VersionHandle version_handle = _client->get_version_handle();
201
while (true) {
202
// Find an adjacent predecessor and successor node pair.
203
successor = find_adjacent<Node, VersionHandle, SearchPolicy>(head, tail, &predecessor, version_handle, predicate);
204
if (successor == tail) {
205
return NULL;
206
}
207
// Invariant: predecessor --> successor
208
// Invariant (optional: key-based total order): predecessor->key() < key && key <= successor->key()
209
// It is the successor node that is to be removed.
210
// We first attempt to reserve (logically excise) the successor node.
211
successor_next = mark_for_removal(successor);
212
if (successor_next != NULL) {
213
break;
214
}
215
}
216
// Invariant: predecessor --> (successor) --> successor_next
217
// Successor node now logically excised.
218
assert(is_marked_for_removal(successor->_next), "invariant");
219
// Now attempt to physically excise the successor node.
220
// If the cas fails, we can optimize for the slow path if we know we are not performing
221
// insertions from the head. Then a failed cas results not from a new node being inserted,
222
// but only because another thread excised us already.
223
if (!cas(&predecessor->_next, successor, successor_next) && insert_is_head) {
224
// Physically excise using slow path, can be completed asynchronously by other threads.
225
Identity<Node> excise(successor);
226
find_adjacent<Node, VersionHandle, Identity>(head, tail, &predecessor, version_handle, excise);
227
}
228
if (last != NULL && Atomic::load_acquire(&last->_next) == successor) {
229
guarantee(!insert_is_head, "invariant");
230
guarantee(successor_next == tail, "invariant");
231
LastNode<Node> excise;
232
find_adjacent<Node, VersionHandle, LastNode>(last, tail, &predecessor, version_handle, excise);
233
// Invariant: successor excised from last list
234
}
235
// Commit the modification back to the version control system.
236
// It blocks until all checkouts for versions earlier than the commit have been released.
237
version_handle->commit();
238
// At this point we know there can be no references onto the excised node. It is safe, enjoy it.
239
return successor;
240
}
241
242
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
243
bool JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::in_list(const typename Client::Node* node,
244
typename Client::Node* head,
245
const typename Client::Node* tail) const {
246
assert(head != NULL, "invariant");
247
assert(tail != NULL, "invariant");
248
assert(head != tail, "invariant");
249
VersionHandle version_handle = _client->get_version_handle();
250
const Node* current = head;
251
version_handle->checkout();
252
const Node* next = Atomic::load_acquire(&current->_next);
253
while (true) {
254
if (!is_marked_for_removal(next)) {
255
if (current == node) {
256
return true;
257
}
258
}
259
current = unmask(next);
260
if (current == tail) break;
261
next = current->_next;
262
}
263
return false;
264
}
265
266
template <typename Client, template <typename> class SearchPolicy, typename AllocPolicy>
267
template <typename Callback>
268
inline void JfrConcurrentLinkedListHost<Client, SearchPolicy, AllocPolicy>::iterate(typename Client::Node* head,
269
const typename Client::Node* tail,
270
Callback& cb) {
271
assert(head != NULL, "invariant");
272
assert(tail != NULL, "invariant");
273
assert(head != tail, "invariant");
274
VersionHandle version_handle = _client->get_version_handle();
275
NodePtr current = head;
276
version_handle->checkout();
277
NodePtr next = Atomic::load_acquire(&current->_next);
278
while (true) {
279
if (!is_marked_for_removal(next)) {
280
if (!cb.process(current)) {
281
return;
282
}
283
}
284
current = unmask(next);
285
if (current == tail) break;
286
next = current->_next;
287
}
288
}
289
290
#endif // SHARE_JFR_UTILITIES_JFRCONCURRENTLINKEDLISTHOST_INLINE_HPP
291
292