Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,17 @@
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.remoting.ChannelClosedException;
import hudson.remoting.DaemonThreadFactory;
import hudson.remoting.NamingThreadFactory;
import java.io.EOFException;
import java.lang.ref.Cleaner;
import java.nio.channels.ClosedChannelException;
import java.util.stream.Stream;
import jenkins.util.Timer;

/**
* A stream which will be flushed before garbage collection.
Expand All @@ -48,10 +47,12 @@
final class GCFlushedOutputStream extends FilterOutputStream {

private static final Logger LOGGER = Logger.getLogger(GCFlushedOutputStream.class.getName());
private static final Cleaner CLEANER = Cleaner.create(
new NamingThreadFactory(new DaemonThreadFactory(), GCFlushedOutputStream.class.getName() + ".CLEANER"));

GCFlushedOutputStream(OutputStream out) {
super(out);
FlushRef.register(this, out);
CLEANER.register(this, new CleanerTask(out));
}

@Override public void write(@NonNull byte[] b, int off, int len) throws IOException {
Expand Down Expand Up @@ -80,40 +81,23 @@ private static boolean isClosedChannelException(Throwable t) {
/**
* Flushes streams prior to garbage collection.
* ({@link BufferedOutputStream} does not do this automatically.)
* TODO Java 9+ could use java.util.Cleaner
*/
private static final class FlushRef extends PhantomReference<GCFlushedOutputStream> {

private static final ReferenceQueue<GCFlushedOutputStream> rq = new ReferenceQueue<>();

static {
Timer.get().scheduleWithFixedDelay(() -> {
while (true) {
FlushRef ref = (FlushRef) rq.poll();
if (ref == null) {
break;
}
LOGGER.log(Level.FINE, "flushing {0}", ref.out);
try {
ref.out.flush();
} catch (IOException x) {
LOGGER.log(isClosedChannelException(x) ? Level.FINE : Level.WARNING, null, x);
}
}
}, 0, 10, TimeUnit.SECONDS);
}

static void register(GCFlushedOutputStream fos, OutputStream out) {
new FlushRef(fos, out, rq).enqueue();
}

private static final class CleanerTask implements Runnable {
private final OutputStream out;

private FlushRef(GCFlushedOutputStream fos, OutputStream out, ReferenceQueue<GCFlushedOutputStream> rq) {
super(fos, rq);
public CleanerTask(OutputStream out) {
this.out = out;
}

@Override
public void run() {
LOGGER.log(Level.FINE, "flushing {0}", out);
try {
out.flush();
} catch (IOException x) {
LOGGER.log(isClosedChannelException(x) ? Level.FINE : Level.WARNING, null, x);
}
}
}

}
Loading