Skip to content

Commit 4d4e51c

Browse files
david-beaumontdfuch
authored andcommitted
8365483: Test sun/rmi/runtime/Log/6409194/NoConsoleOutput.java sometimes fails
Reviewed-by: dfuchs, jpai
1 parent edae355 commit 4d4e51c

File tree

2 files changed

+145
-5
lines changed

2 files changed

+145
-5
lines changed

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,16 @@ public void publish(LogRecord record) {
214214

215215
try {
216216
synchronized (this) {
217+
// Re-check writer between isLoggable() and here.
217218
Writer writer = this.writer;
218-
if (!doneHeader) {
219-
writer.write(formatter.getHead(this));
220-
doneHeader = true;
219+
if (writer != null) {
220+
if (!doneHeader) {
221+
writer.write(formatter.getHead(this));
222+
doneHeader = true;
223+
}
224+
writer.write(msg);
225+
synchronousPostWriteHook();
221226
}
222-
writer.write(msg);
223-
synchronousPostWriteHook();
224227
}
225228
} catch (Exception ex) {
226229
// We don't want to throw an exception here, but we
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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+
import java.io.ByteArrayOutputStream;
25+
import java.util.ArrayList;
26+
import java.util.List;
27+
import java.util.concurrent.Callable;
28+
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.ExecutorService;
30+
import java.util.concurrent.Executors;
31+
import java.util.concurrent.Future;
32+
import java.util.concurrent.atomic.AtomicReference;
33+
import java.util.logging.ErrorManager;
34+
import java.util.logging.Level;
35+
import java.util.logging.LogRecord;
36+
import java.util.logging.SimpleFormatter;
37+
import java.util.logging.StreamHandler;
38+
39+
import org.junit.jupiter.api.Test;
40+
41+
/*
42+
* @test
43+
* @bug 8365483
44+
* @summary verify that concurrent calls to publish() and close() on a
45+
* StreamHandler do not cause unexpected exceptions
46+
* @run junit StreamHandlerRacyCloseTest
47+
*/
48+
public class StreamHandlerRacyCloseTest {
49+
50+
private static final class ExceptionTrackingErrorManager extends ErrorManager {
51+
private final AtomicReference<Exception> firstError = new AtomicReference<>();
52+
53+
@Override
54+
public void error(String msg, Exception ex, int code) {
55+
// just track one/first exception, that's good enough for this test
56+
this.firstError.compareAndSet(null, new RuntimeException(msg, ex));
57+
}
58+
}
59+
60+
@Test
61+
void testRacyClose() throws Exception {
62+
final int numTimes = 100;
63+
try (ExecutorService executor = Executors.newFixedThreadPool(numTimes)) {
64+
final List<Callable<Void>> tasks = new ArrayList<>();
65+
for (int i = 1; i <= numTimes; i++) {
66+
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
67+
// construct a StreamHandler with an ErrorManager which propagates
68+
// any errors that happen during publish()
69+
final StreamHandler handler = new StreamHandler(baos, new SimpleFormatter());
70+
handler.setErrorManager(new ExceptionTrackingErrorManager());
71+
72+
final CountDownLatch latch = new CountDownLatch(2);
73+
// create a publisher and closer task which will run concurrently
74+
tasks.add(new Publisher(handler, latch));
75+
tasks.add(new Closer(handler, latch));
76+
}
77+
// submit the tasks and expect successful completion of each
78+
final List<Future<Void>> completed = executor.invokeAll(tasks);
79+
for (var f : completed) {
80+
f.get();
81+
}
82+
}
83+
}
84+
85+
private static final class Closer implements Callable<Void> {
86+
private final StreamHandler handler;
87+
private final CountDownLatch startLatch;
88+
89+
private Closer(final StreamHandler handler, final CountDownLatch startLatch) {
90+
this.handler = handler;
91+
this.startLatch = startLatch;
92+
}
93+
94+
@Override
95+
public Void call() throws Exception {
96+
// notify the other task of our readiness
97+
this.startLatch.countDown();
98+
// wait for the other task to arrive
99+
this.startLatch.await();
100+
// close the handler
101+
this.handler.close();
102+
// propagate any exception that may have been caught by the error manager
103+
final var errMgr = (ExceptionTrackingErrorManager) this.handler.getErrorManager();
104+
if (errMgr.firstError.get() != null) {
105+
throw errMgr.firstError.get();
106+
}
107+
return null;
108+
}
109+
}
110+
111+
private static final class Publisher implements Callable<Void> {
112+
private final StreamHandler handler;
113+
private final CountDownLatch startLatch;
114+
115+
private Publisher(final StreamHandler handler, final CountDownLatch startLatch) {
116+
this.handler = handler;
117+
this.startLatch = startLatch;
118+
}
119+
120+
@Override
121+
public Void call() throws Exception {
122+
final LogRecord record = new LogRecord(Level.WARNING, "hello world");
123+
// notify the other task of our readiness
124+
this.startLatch.countDown();
125+
// wait for the other task to arrive
126+
this.startLatch.await();
127+
// publish the record
128+
this.handler.publish(record);
129+
// propagate any exception that may have been caught by the error manager
130+
final var errMgr = (ExceptionTrackingErrorManager) this.handler.getErrorManager();
131+
if (errMgr.firstError.get() != null) {
132+
throw errMgr.firstError.get();
133+
}
134+
return null;
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)