Skip to content

Commit d33b987

Browse files
committed
Use a phantom reference queue rather than overriding finalize.
1 parent 8ab09d1 commit d33b987

File tree

1 file changed

+43
-8
lines changed

1 file changed

+43
-8
lines changed

src/main/java/org/jenkinsci/plugins/workflow/log/DelayBufferedOutputStream.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import java.io.FilterOutputStream;
2929
import java.io.IOException;
3030
import java.io.OutputStream;
31+
import java.lang.ref.PhantomReference;
3132
import java.lang.ref.Reference;
33+
import java.lang.ref.ReferenceQueue;
3234
import java.lang.ref.WeakReference;
3335
import java.util.concurrent.TimeUnit;
3436
import java.util.logging.Level;
@@ -65,6 +67,7 @@ private Tuning() {}
6567
super(new FlushControlledOutputStream(out), tuning.bufferSize);
6668
this.tuning = tuning;
6769
recurrencePeriod = tuning.minRecurrencePeriod;
70+
FlushRef.register(this, out);
6871
reschedule();
6972
}
7073

@@ -95,14 +98,6 @@ void flushAndReschedule() {
9598
reschedule();
9699
}
97100

98-
@SuppressWarnings("FinalizeDeclaration") // not ideal, but PhantomReference is more of a hassle
99-
@Override protected void finalize() throws Throwable {
100-
super.finalize();
101-
Thread.sleep(100); // TODO for FileLogStorageTest#remoting
102-
// Odd that this is not the default behavior for BufferedOutputStream.
103-
flush();
104-
}
105-
106101
private static final class Flush implements Runnable {
107102

108103
/** Since there is no explicit close event, just keep flushing periodically until the stream is collected. */
@@ -121,6 +116,46 @@ private static final class Flush implements Runnable {
121116

122117
}
123118

119+
//
120+
/**
121+
* Flushes streams prior to garbage collection.
122+
* TODO Java 9+ could use java.util.Cleaner
123+
*/
124+
private static final class FlushRef extends PhantomReference<DelayBufferedOutputStream> {
125+
126+
private static final ReferenceQueue<DelayBufferedOutputStream> rq = new ReferenceQueue<>();
127+
128+
static {
129+
Timer.get().scheduleWithFixedDelay(() -> {
130+
while (true) {
131+
FlushRef ref = (FlushRef) rq.poll();
132+
if (ref == null) {
133+
break;
134+
}
135+
LOGGER.log(Level.FINE, "cleaning up phantom {0}", ref.out);
136+
try {
137+
// Odd that this is not the default behavior for BufferedOutputStream.
138+
ref.out.flush();
139+
} catch (IOException x) {
140+
LOGGER.log(Level.WARNING, null, x);
141+
}
142+
}
143+
}, 0, 10, TimeUnit.SECONDS);
144+
}
145+
146+
static void register(DelayBufferedOutputStream dbos, OutputStream out) {
147+
new FlushRef(dbos, out, rq).enqueue();
148+
}
149+
150+
private final OutputStream out;
151+
152+
private FlushRef(DelayBufferedOutputStream dbos, OutputStream out, ReferenceQueue<DelayBufferedOutputStream> rq) {
153+
super(dbos, rq);
154+
this.out = out;
155+
}
156+
157+
}
158+
124159
/** @see DelayBufferedOutputStream#flushBuffer */
125160
private static final class FlushControlledOutputStream extends FilterOutputStream {
126161

0 commit comments

Comments
 (0)