Skip to content

Commit f1258f9

Browse files
committedFeb 17, 2025
8349755: Fix corner case issues in async UL
Reviewed-by: dholmes, aboldtch
1 parent b1b4828 commit f1258f9

File tree

7 files changed

+145
-24
lines changed

7 files changed

+145
-24
lines changed
 

‎src/hotspot/share/logging/logAsyncWriter.cpp

+71-7
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,34 @@
3131
#include "runtime/atomic.hpp"
3232
#include "runtime/os.inline.hpp"
3333

34+
DEBUG_ONLY(bool AsyncLogWriter::ignore_recursive_logging = false;)
35+
3436
class AsyncLogWriter::AsyncLogLocker : public StackObj {
35-
public:
37+
static Thread* _holder;
38+
public:
39+
static Thread* current_holder() { return _holder; }
3640
AsyncLogLocker() {
3741
assert(_instance != nullptr, "AsyncLogWriter::_lock is unavailable");
3842
_instance->_lock.lock();
43+
_holder = Thread::current_or_null();
3944
}
4045

4146
~AsyncLogLocker() {
47+
assert(_holder == Thread::current_or_null(), "must be");
48+
_holder = nullptr;
4249
_instance->_lock.unlock();
4350
}
51+
52+
void wait() {
53+
Thread* saved_holder = _holder;
54+
_holder = nullptr;
55+
_instance->_lock.wait(0/* no timeout */);
56+
_holder = saved_holder;
57+
}
4458
};
4559

60+
Thread* AsyncLogWriter::AsyncLogLocker::_holder = nullptr;
61+
4662
// LogDecorator::None applies to 'constant initialization' because of its constexpr constructor.
4763
const LogDecorations& AsyncLogWriter::None = LogDecorations(LogLevel::Warning, LogTagSetMapping<LogTag::__NO_TAG>::tagset(),
4864
LogDecorators::None);
@@ -84,19 +100,67 @@ void AsyncLogWriter::enqueue_locked(LogFileStreamOutput* output, const LogDecora
84100
_lock.notify();
85101
}
86102

87-
void AsyncLogWriter::enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg) {
103+
// This function checks for cases where continuing with asynchronous logging may lead to stability issues, such as a deadlock.
104+
// If this returns false then we give up on logging asynchronously and do so synchronously instead.
105+
bool AsyncLogWriter::is_enqueue_allowed() {
106+
AsyncLogWriter* alw = AsyncLogWriter::instance();
107+
Thread* holding_thread = AsyncLogWriter::AsyncLogLocker::current_holder();
108+
Thread* this_thread = Thread::current_or_null();
109+
if (this_thread == nullptr) {
110+
// The current thread is unattached.
111+
return false;
112+
}
113+
114+
if (holding_thread == this_thread) {
115+
// A thread, while enqueuing a message, has attempted to log something.
116+
// Do not log while holding the Async log lock.
117+
// Try to catch possible occurrences in debug builds.
118+
#ifdef ASSERT
119+
if (!AsyncLogWriter::ignore_recursive_logging) {
120+
ShouldNotReachHere();
121+
}
122+
#endif // ASSERT
123+
return false;
124+
}
125+
126+
if (alw == nullptr) {
127+
// There is no AsyncLogWriter instance yet.
128+
return false;
129+
}
130+
131+
if (this_thread == alw) {
132+
// The async log producer is attempting to log, leading to recursive logging.
133+
return false;
134+
}
135+
136+
return true;
137+
}
138+
139+
bool AsyncLogWriter::enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg) {
140+
if (!is_enqueue_allowed()) {
141+
return false;
142+
}
143+
88144
AsyncLogLocker locker;
89-
enqueue_locked(&output, decorations, msg);
145+
DEBUG_ONLY(log_debug(deathtest)("Induce a recursive log for testing (for crashing)");)
146+
DEBUG_ONLY(log_debug(deathtest2)("Induce a recursive log for testing");)
147+
AsyncLogWriter::instance()->enqueue_locked(&output, decorations, msg);
148+
return true;
90149
}
91150

92151
// LogMessageBuffer consists of a multiple-part/multiple-line message.
93152
// The lock here guarantees its integrity.
94-
void AsyncLogWriter::enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator) {
95-
AsyncLogLocker locker;
153+
bool AsyncLogWriter::enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator) {
154+
if (!is_enqueue_allowed()) {
155+
return false;
156+
}
96157

158+
// If we get here we know the AsyncLogWriter is initialized.
159+
AsyncLogLocker locker;
97160
for (; !msg_iterator.is_at_end(); msg_iterator++) {
98-
enqueue_locked(&output, msg_iterator.decorations(), msg_iterator.message());
161+
AsyncLogWriter::instance()->enqueue_locked(&output, msg_iterator.decorations(), msg_iterator.message());
99162
}
163+
return true;
100164
}
101165

102166
AsyncLogWriter::AsyncLogWriter()
@@ -155,7 +219,7 @@ void AsyncLogWriter::run() {
155219
AsyncLogLocker locker;
156220

157221
while (!_data_available) {
158-
_lock.wait(0/* no timeout */);
222+
locker.wait();
159223
}
160224
// Only doing a swap and statistics under the lock to
161225
// guarantee that I/O jobs don't block logsites.

‎src/hotspot/share/logging/logAsyncWriter.hpp

+6-3
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,12 @@ class AsyncLogWriter : public NonJavaThread {
195195
~BufferUpdater();
196196
};
197197

198-
public:
199-
void enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg);
200-
void enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator);
198+
static bool is_enqueue_allowed();
199+
200+
public:
201+
DEBUG_ONLY(static bool ignore_recursive_logging;)
202+
static bool enqueue(LogFileStreamOutput& output, const LogDecorations& decorations, const char* msg);
203+
static bool enqueue(LogFileStreamOutput& output, LogMessageBuffer::Iterator msg_iterator);
201204

202205
static AsyncLogWriter* instance();
203206
static void initialize();

‎src/hotspot/share/logging/logFileOutput.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -292,9 +292,7 @@ int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
292292
return 0;
293293
}
294294

295-
AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
296-
if (aio_writer != nullptr) {
297-
aio_writer->enqueue(*this, decorations, msg);
295+
if (AsyncLogWriter::enqueue(*this, decorations, msg)) {
298296
return 0;
299297
}
300298

@@ -306,10 +304,7 @@ int LogFileOutput::write(LogMessageBuffer::Iterator msg_iterator) {
306304
// An error has occurred with this output, avoid writing to it.
307305
return 0;
308306
}
309-
310-
AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
311-
if (aio_writer != nullptr) {
312-
aio_writer->enqueue(*this, msg_iterator);
307+
if (AsyncLogWriter::enqueue(*this, msg_iterator)) {
313308
return 0;
314309
}
315310

‎src/hotspot/share/logging/logFileStreamOutput.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,7 @@ int LogFileStreamOutput::write_blocking(const LogDecorations& decorations, const
173173
}
174174

175175
int LogFileStreamOutput::write(const LogDecorations& decorations, const char* msg) {
176-
AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
177-
if (aio_writer != nullptr) {
178-
aio_writer->enqueue(*this, decorations, msg);
176+
if (AsyncLogWriter::enqueue(*this, decorations, msg)) {
179177
return 0;
180178
}
181179

@@ -186,9 +184,7 @@ int LogFileStreamOutput::write(const LogDecorations& decorations, const char* ms
186184
}
187185

188186
int LogFileStreamOutput::write(LogMessageBuffer::Iterator msg_iterator) {
189-
AsyncLogWriter* aio_writer = AsyncLogWriter::instance();
190-
if (aio_writer != nullptr) {
191-
aio_writer->enqueue(*this, msg_iterator);
187+
if (AsyncLogWriter::enqueue(*this, msg_iterator)) {
192188
return 0;
193189
}
194190

‎src/hotspot/share/logging/logTag.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class outputStream;
6868
LOG_TAG(data) \
6969
LOG_TAG(datacreation) \
7070
LOG_TAG(dcmd) \
71+
DEBUG_ONLY(LOG_TAG(deathtest)) /* Log Internal death test tag */ \
72+
DEBUG_ONLY(LOG_TAG(deathtest2)) /* Log Internal death test tag */ \
7173
LOG_TAG(decoder) \
7274
LOG_TAG(defaultmethods) \
7375
LOG_TAG(deoptimization) \

‎src/hotspot/share/logging/logTagSet.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*
2323
*/
2424
#include "jvm.h"
25+
#include "logging/logAsyncWriter.hpp"
2526
#include "logging/logDecorations.hpp"
2627
#include "logging/logFileStreamOutput.hpp"
2728
#include "logging/logLevel.hpp"
@@ -77,7 +78,13 @@ void LogTagSet::log(LogLevelType level, const char* msg) {
7778
// the implied memory order of Atomic::add().
7879
LogOutputList::Iterator it = _output_list.iterator(level);
7980
LogDecorations decorations(level, *this, _decorators);
80-
81+
#ifdef ASSERT
82+
// If we log for tag deathtest2 then we're testing that recursive logging works.
83+
// In this case, do not crash when detecting recursive logging.
84+
if (this->contains(LogTagType::_deathtest2)) {
85+
AsyncLogWriter::ignore_recursive_logging = true;
86+
}
87+
#endif
8188
for (; it != _output_list.end(); it++) {
8289
(*it)->write(decorations, msg);
8390
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright (c) 2025, 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+
* @test
26+
* @bug 8349755
27+
* @summary Perform recursive logging in async UL on purpose
28+
* @requires vm.flagless
29+
* @requires vm.debug
30+
* @library /test/lib
31+
* @modules java.base/jdk.internal.misc
32+
* java.management
33+
* @run driver AsyncDeathTest
34+
*/
35+
36+
import jdk.test.lib.process.ProcessTools;
37+
import jdk.test.lib.process.OutputAnalyzer;
38+
39+
public class AsyncDeathTest {
40+
public static void main(String[] args) throws Exception {
41+
// For deathtest we expect the VM to reach ShouldNotReachHere() and die
42+
ProcessBuilder pb =
43+
ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:async", "-Xlog:os,deathtest=debug", "-XX:-CreateCoredumpOnCrash", "--version");
44+
OutputAnalyzer output = new OutputAnalyzer(pb.start());
45+
output.shouldNotHaveExitValue(0);
46+
output.shouldNotContain("Induce a recursive log for testing");
47+
// For deathtest2 we expect the VM to ignore that recursive logging has been detected and is handled by printing synchronously.
48+
ProcessBuilder pb2 =
49+
ProcessTools.createLimitedTestJavaProcessBuilder("-Xlog:async", "-Xlog:os,deathtest2=debug", "--version");
50+
OutputAnalyzer output2 = new OutputAnalyzer(pb2.start());
51+
output2.shouldHaveExitValue(0);
52+
output2.shouldContain("Induce a recursive log for testing");
53+
}
54+
}

0 commit comments

Comments
 (0)
Please sign in to comment.