Skip to content

Commit 57df89c

Browse files
author
Stuart Marks
committed
8353684: [BACKOUT] j.u.l.Handler classes create deadlock risk via synchronized publish() method
Reviewed-by: dholmes
1 parent ebcb9a8 commit 57df89c

File tree

8 files changed

+23
-445
lines changed

8 files changed

+23
-445
lines changed

src/java.logging/share/classes/java/util/logging/ConsoleHandler.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,6 @@ public ConsoleHandler() {
8787
* The logging request was made initially to a {@code Logger} object,
8888
* which initialized the {@code LogRecord} and forwarded it here.
8989
*
90-
* @implSpec This method is not synchronized, and subclasses must not define
91-
* overridden {@code publish()} methods to be {@code synchronized} if they
92-
* call {@code super.publish()} or format user arguments. See the
93-
* {@linkplain Handler##threadSafety discussion in java.util.logging.Handler}
94-
* for more information.
95-
*
9690
* @param record description of the log event. A null record is
9791
* silently ignored and is not published
9892
*/

src/java.logging/share/classes/java/util/logging/FileHandler.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -739,14 +739,11 @@ private synchronized void rotate() {
739739
* silently ignored and is not published
740740
*/
741741
@Override
742-
public void publish(LogRecord record) {
742+
public synchronized void publish(LogRecord record) {
743+
if (!isLoggable(record)) {
744+
return;
745+
}
743746
super.publish(record);
744-
}
745-
746-
@Override
747-
void synchronousPostWriteHook() {
748-
// no need to synchronize here, this method is called from within a
749-
// synchronized block.
750747
flush();
751748
if (limit > 0 && (meter.written >= limit || meter.written < 0)) {
752749
rotate();

src/java.logging/share/classes/java/util/logging/Handler.java

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -43,25 +43,10 @@
4343
* and {@code Level}. See the specific documentation for each concrete
4444
* {@code Handler} class.
4545
*
46-
* <h2><a id=threadSafety>Thread Safety and Deadlock Risk in Handlers</a></h2>
47-
*
48-
* Implementations of {@code Handler} should be thread-safe. Handlers are
49-
* expected to be invoked concurrently from arbitrary threads. However,
50-
* over-use of synchronization may result in unwanted thread contention,
51-
* performance issues or even deadlocking.
52-
* <p>
53-
* In particular, subclasses should avoid acquiring locks around code which
54-
* calls back to arbitrary user-supplied objects, especially during log record
55-
* formatting. Holding a lock around any such callbacks creates a deadlock risk
56-
* between logging code and user code.
57-
* <p>
58-
* As such, general purpose {@code Handler} subclasses should not synchronize
59-
* their {@link #publish(LogRecord)} methods, or call {@code super.publish()}
60-
* while holding locks, since these are typically expected to need to process
61-
* and format user-supplied arguments.
6246
*
6347
* @since 1.4
6448
*/
49+
6550
public abstract class Handler {
6651
private static final int offValue = Level.OFF.intValue();
6752

@@ -138,10 +123,6 @@ protected Handler() { }
138123
* <p>
139124
* The {@code Handler} is responsible for formatting the message, when and
140125
* if necessary. The formatting should include localization.
141-
* <p>
142-
* @apiNote To avoid the risk of deadlock, implementations of this method
143-
* should avoid holding any locks while calling out to application code,
144-
* such as the formatting of {@code LogRecord}.
145126
*
146127
* @param record description of the log event. A null record is
147128
* silently ignored and is not published

src/java.logging/share/classes/java/util/logging/SocketHandler.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -168,17 +168,14 @@ public synchronized void close() {
168168
/**
169169
* Format and publish a {@code LogRecord}.
170170
*
171-
* @implSpec This method is not synchronized, and subclasses must not define
172-
* overridden {@code publish()} methods to be {@code synchronized} if they
173-
* call {@code super.publish()} or format user arguments. See the
174-
* {@linkplain Handler##threadSafety discussion in java.util.logging.Handler}
175-
* for more information.
176-
*
177171
* @param record description of the log event. A null record is
178172
* silently ignored and is not published
179173
*/
180174
@Override
181-
public void publish(LogRecord record) {
175+
public synchronized void publish(LogRecord record) {
176+
if (!isLoggable(record)) {
177+
return;
178+
}
182179
super.publish(record);
183180
flush();
184181
}

src/java.logging/share/classes/java/util/logging/StreamHandler.java

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -180,31 +180,17 @@ public synchronized void setEncoding(String encoding)
180180
* {@code OutputStream}, the {@code Formatter}'s "head" string is
181181
* written to the stream before the {@code LogRecord} is written.
182182
*
183-
* @implSpec This method avoids acquiring locks during {@code LogRecord}
184-
* formatting, but {@code this} instance is synchronized when writing to the
185-
* output stream. To avoid deadlock risk, subclasses must not hold locks
186-
* while calling {@code super.publish()}. Specifically, subclasses must
187-
* not define the overridden {@code publish()} method to be
188-
* {@code synchronized} if they call {@code super.publish()}.
189-
*
190183
* @param record description of the log event. A null record is
191184
* silently ignored and is not published
192185
*/
193186
@Override
194-
public void publish(LogRecord record) {
195-
if (!isLoggable(record)) {
187+
public synchronized void publish(LogRecord record) {
188+
if (!isLoggable(record)) {
196189
return;
197190
}
198-
// Read once for consistency (whether in or outside the locked region
199-
// is not important).
200-
Formatter formatter = getFormatter();
201-
// JDK-8349206: To avoid deadlock risk, it is essential that the handler
202-
// is not locked while formatting the log record. Methods such as
203-
// reportError() and isLoggable() are defined to be thread safe, so we
204-
// can restrict locking to just writing the message.
205191
String msg;
206192
try {
207-
msg = formatter.format(record);
193+
msg = getFormatter().format(record);
208194
} catch (Exception ex) {
209195
// We don't want to throw an exception here, but we
210196
// report the exception to any registered ErrorManager.
@@ -213,33 +199,19 @@ public void publish(LogRecord record) {
213199
}
214200

215201
try {
216-
synchronized(this) {
217-
Writer writer = this.writer;
218-
if (!doneHeader) {
219-
writer.write(formatter.getHead(this));
220-
doneHeader = true;
221-
}
222-
writer.write(msg);
223-
synchronousPostWriteHook();
202+
Writer writer = this.writer;
203+
if (!doneHeader) {
204+
writer.write(getFormatter().getHead(this));
205+
doneHeader = true;
224206
}
207+
writer.write(msg);
225208
} catch (Exception ex) {
226209
// We don't want to throw an exception here, but we
227210
// report the exception to any registered ErrorManager.
228211
reportError(null, ex, ErrorManager.WRITE_FAILURE);
229212
}
230213
}
231214

232-
/**
233-
* Overridden by other handlers in this package to facilitate synchronous
234-
* post-write behaviour. If other handlers need similar functionality, it
235-
* might be feasible to make this method protected (see JDK-8349206), but
236-
* please find a better name if you do ;).
237-
*/
238-
void synchronousPostWriteHook() {
239-
// Empty by default. We could do:
240-
// assert Thread.holdsLock(this);
241-
// but this is already covered by unit tests.
242-
}
243215

244216
/**
245217
* Check if this {@code Handler} would actually log a given {@code LogRecord}.
@@ -277,17 +249,15 @@ public synchronized void flush() {
277249
}
278250
}
279251

280-
// Called synchronously with "this" handler instance locked.
281252
private void flushAndClose() {
282253
Writer writer = this.writer;
283254
if (writer != null) {
284-
Formatter formatter = getFormatter();
285255
try {
286256
if (!doneHeader) {
287-
writer.write(formatter.getHead(this));
257+
writer.write(getFormatter().getHead(this));
288258
doneHeader = true;
289259
}
290-
writer.write(formatter.getTail(this));
260+
writer.write(getFormatter().getTail(this));
291261
writer.flush();
292262
writer.close();
293263
} catch (Exception ex) {

test/jdk/java/util/logging/Handler/StreamHandlerLockingTest.java

Lines changed: 0 additions & 105 deletions
This file was deleted.

test/jdk/java/util/logging/Handler/java.logging/java/util/logging/TestStreamHandler.java

Lines changed: 0 additions & 54 deletions
This file was deleted.

0 commit comments

Comments
 (0)