From 5dcd3bae5a40a8822ed98f665273887542954c2a Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Tue, 17 Oct 2023 09:00:44 -0500 Subject: [PATCH] Write combining through flush batching Use a counter to track how many threads are waiting to write; only the last writer flushes. --- .../logmanager/handlers/WriterHandler.java | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/jboss/logmanager/handlers/WriterHandler.java b/src/main/java/org/jboss/logmanager/handlers/WriterHandler.java index 45ccb56b..c2149960 100644 --- a/src/main/java/org/jboss/logmanager/handlers/WriterHandler.java +++ b/src/main/java/org/jboss/logmanager/handlers/WriterHandler.java @@ -23,6 +23,9 @@ import java.io.Closeable; import java.io.Flushable; import java.io.Writer; +import java.lang.invoke.ConstantBootstraps; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.logging.ErrorManager; import java.util.logging.Formatter; @@ -33,9 +36,17 @@ * A handler which writes to any {@code Writer}. */ public class WriterHandler extends ExtHandler { + private static final VarHandle waitingWritersHandle = ConstantBootstraps.fieldVarHandle(MethodHandles.lookup(), "waitingWriters", VarHandle.class, WriterHandler.class, int.class); private volatile boolean checkHeadEncoding = true; private volatile boolean checkTailEncoding = true; + /** + * The number of waiting writers. + * Accessed via {@link #waitingWritersHandle} (not unused). + * @see #waitingWritersHandle + */ + @SuppressWarnings("unused") + private volatile int waitingWriters; private Writer writer; /** @@ -54,24 +65,46 @@ protected void doPublish(final ExtLogRecord record) { reportError("Formatting error", ex, ErrorManager.FORMAT_FAILURE); return; } - if (formatted.length() == 0) { + if (formatted.isEmpty()) { // nothing to write; don't bother return; } try { + waitingWritersHandle.getAndAdd(this, 1); lock.lock(); try { if (writer == null) { + // nothing to write to + waitingWritersHandle.getAndAdd(this, -1); return; } - preWrite(record); + try { + preWrite(record); + } catch (Throwable t) { + // the decrement was missed + waitingWritersHandle.getAndAdd(this, -1); + throw t; + } + // writer may have been changed by preWrite; re-check it final Writer writer = this.writer; if (writer == null) { + // nothing to write to + waitingWritersHandle.getAndAdd(this, -1); return; } - writer.write(formatted); - // only flush if something was written - super.doPublish(record); + try { + writer.write(formatted); + } catch (Throwable t) { + // the decrement was missed + waitingWritersHandle.getAndAdd(this, -1); + throw t; + } + // only flush if something was written and we're the last one + int remainingWaitersIncludingUs = (int) waitingWritersHandle.getAndAdd(this, -1); + // at this point the count is reconciled + if (remainingWaitersIncludingUs == 1 && isAutoFlush()) { + flush(); + } } finally { lock.unlock(); }