Wed, 17 Jun 2020 11:43:05 +0300
8220293: Deadlock in JFR string pool
Reviewed-by: rehn, egahlin
1 /*
2 * Copyright (c) 2011, 2019, 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 */
25 #include "precompiled.hpp"
26 #include "jfr/recorder/storage/jfrBuffer.hpp"
27 #include "runtime/atomic.inline.hpp"
28 #include "runtime/orderAccess.inline.hpp"
29 #include "runtime/thread.inline.hpp"
30 #include "jfr/recorder/jfrRecorder.hpp"
32 static const u1* const MUTEX_CLAIM = NULL;
34 JfrBuffer::JfrBuffer() : _next(NULL),
35 _prev(NULL),
36 _identity(NULL),
37 _pos(NULL),
38 _top(NULL),
39 _flags(0),
40 _header_size(0),
41 _size(0) {}
43 bool JfrBuffer::initialize(size_t header_size, size_t size, const void* id /* NULL */) {
44 _header_size = (u2)header_size;
45 _size = (u4)(size / BytesPerWord);
46 assert(_identity == NULL, "invariant");
47 _identity = id;
48 set_pos(start());
49 set_top(start());
50 assert(_next == NULL, "invariant");
51 assert(free_size() == size, "invariant");
52 assert(!transient(), "invariant");
53 assert(!lease(), "invariant");
54 assert(!retired(), "invariant");
55 return true;
56 }
58 void JfrBuffer::reinitialize() {
59 assert(!lease(), "invariant");
60 assert(!transient(), "invariant");
61 set_pos(start());
62 clear_retired();
63 set_top(start());
64 }
66 void JfrBuffer::concurrent_reinitialization() {
67 concurrent_top();
68 assert(!lease(), "invariant");
69 assert(!transient(), "invariant");
70 set_pos(start());
71 set_concurrent_top(start());
72 clear_retired();
73 }
75 size_t JfrBuffer::discard() {
76 size_t discard_size = unflushed_size();
77 set_top(pos());
78 return discard_size;
79 }
81 const u1* JfrBuffer::stable_top() const {
82 const u1* current_top;
83 do {
84 current_top = (const u1*)OrderAccess::load_ptr_acquire(&_top);
85 } while (MUTEX_CLAIM == current_top);
86 return current_top;
87 }
89 const u1* JfrBuffer::top() const {
90 return _top;
91 }
93 void JfrBuffer::set_top(const u1* new_top) {
94 _top = new_top;
95 }
97 const u1* JfrBuffer::concurrent_top() const {
98 do {
99 const u1* current_top = stable_top();
100 if (Atomic::cmpxchg_ptr((void*)MUTEX_CLAIM, &_top, (void*)current_top) == current_top) {
101 return current_top;
102 }
103 } while (true);
104 }
106 void JfrBuffer::set_concurrent_top(const u1* new_top) {
107 assert(new_top != MUTEX_CLAIM, "invariant");
108 assert(new_top <= end(), "invariant");
109 assert(new_top >= start(), "invariant");
110 assert(top() == MUTEX_CLAIM, "invariant");
111 OrderAccess::release_store_ptr(&_top, (void*)new_top);
112 }
114 size_t JfrBuffer::unflushed_size() const {
115 return pos() - stable_top();
116 }
118 void JfrBuffer::acquire(const void* id) {
119 assert(id != NULL, "invariant");
120 const void* current_id;
121 do {
122 current_id = OrderAccess::load_ptr_acquire(&_identity);
123 } while (current_id != NULL || Atomic::cmpxchg_ptr((void*)id, &_identity, (void*)current_id) != current_id);
124 }
126 bool JfrBuffer::try_acquire(const void* id) {
127 assert(id != NULL, "invariant");
128 const void* const current_id = OrderAccess::load_ptr_acquire(&_identity);
129 return current_id == NULL && Atomic::cmpxchg_ptr((void*)id, &_identity, (void*)current_id) == current_id;
130 }
132 void JfrBuffer::release() {
133 OrderAccess::release_store_ptr(&_identity, (void*)NULL);
134 }
136 void JfrBuffer::clear_identity() {
137 _identity = NULL;
138 }
140 bool JfrBuffer::acquired_by(const void* id) const {
141 return identity() == id;
142 }
144 bool JfrBuffer::acquired_by_self() const {
145 return acquired_by(Thread::current());
146 }
148 #ifdef ASSERT
149 static bool validate_to(const JfrBuffer* const to, size_t size) {
150 assert(to != NULL, "invariant");
151 if (!JfrRecorder::is_shutting_down()) assert(to->acquired_by_self(), "invariant");
152 assert(to->free_size() >= size, "invariant");
153 return true;
154 }
156 static bool validate_concurrent_this(const JfrBuffer* const t, size_t size) {
157 assert(t->top() == MUTEX_CLAIM, "invariant");
158 return true;
159 }
161 static bool validate_this(const JfrBuffer* const t, size_t size) {
162 assert(t->top() + size <= t->pos(), "invariant");
163 return true;
164 }
165 #endif // ASSERT
167 void JfrBuffer::move(JfrBuffer* const to, size_t size) {
168 assert(validate_to(to, size), "invariant");
169 assert(validate_this(this, size), "invariant");
170 const u1* current_top = top();
171 assert(current_top != NULL, "invariant");
172 memcpy(to->pos(), current_top, size);
173 to->set_pos(size);
174 to->release();
175 set_top(current_top + size);
176 }
178 void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t size) {
179 assert(validate_to(to, size), "invariant");
180 const u1* current_top = concurrent_top();
181 assert(validate_concurrent_this(this, size), "invariant");
182 const size_t actual_size = MIN2(size, (size_t)(pos() - current_top));
183 assert(actual_size <= size, "invariant");
184 memcpy(to->pos(), current_top, actual_size);
185 to->set_pos(actual_size);
186 set_pos(start());
187 to->release();
188 set_concurrent_top(start());
189 }
191 enum FLAG {
192 RETIRED = 1,
193 TRANSIENT = 2,
194 LEASE = 4
195 };
197 bool JfrBuffer::transient() const {
198 return (u1)TRANSIENT == (_flags & (u1)TRANSIENT);
199 }
201 void JfrBuffer::set_transient() {
202 _flags |= (u1)TRANSIENT;
203 assert(transient(), "invariant");
204 }
206 void JfrBuffer::clear_transient() {
207 if (transient()) {
208 _flags ^= (u1)TRANSIENT;
209 }
210 assert(!transient(), "invariant");
211 }
213 bool JfrBuffer::lease() const {
214 return (u1)LEASE == (_flags & (u1)LEASE);
215 }
217 void JfrBuffer::set_lease() {
218 _flags |= (u1)LEASE;
219 assert(lease(), "invariant");
220 }
222 void JfrBuffer::clear_lease() {
223 if (lease()) {
224 _flags ^= (u1)LEASE;
225 }
226 assert(!lease(), "invariant");
227 }
229 static u2 load_acquire_flags(const u2* const flags) {
230 return OrderAccess::load_acquire((volatile jushort *)flags);
231 }
233 static void release_store_flags(u2* const flags, u2 new_flags) {
234 OrderAccess::release_store(flags, new_flags);
235 }
237 bool JfrBuffer::retired() const {
238 return (u1)RETIRED == (load_acquire_flags(&_flags) & (u1)RETIRED);
239 }
241 void JfrBuffer::set_retired() {
242 const u2 new_flags = load_acquire_flags(&_flags) | (u1)RETIRED;
243 release_store_flags(&_flags, new_flags);
244 }
246 void JfrBuffer::clear_retired() {
247 u2 new_flags = load_acquire_flags(&_flags);
248 if ((u1)RETIRED == (new_flags & (u1)RETIRED)) {
249 new_flags ^= (u1)RETIRED;
250 release_store_flags(&_flags, new_flags);
251 }
252 }