From 03f4469ae955081bbbadef965306cc26d6826885 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 28 May 2021 11:51:57 +0200 Subject: [PATCH 01/75] use RetainableByteBuffer Signed-off-by: Ludovic Orban --- .../eclipse/jetty/server/HttpConnection.java | 76 +++++++++---------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 728214a75721..1562e15d5f56 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -17,7 +17,6 @@ import java.nio.ByteBuffer; import java.nio.channels.WritePendingException; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.LongAdder; import org.eclipse.jetty.http.BadMessageException; @@ -35,6 +34,7 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -60,8 +60,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; private final HttpParser _parser; - private final AtomicInteger _contentBufferReferences = new AtomicInteger(); - private volatile ByteBuffer _requestBuffer = null; + private volatile RetainableByteBuffer _retainableByteBuffer; private final AsyncReadCallback _asyncReadCallback = new AsyncReadCallback(); private final SendCallback _sendCallback = new SendCallback(); private final boolean _recordHttpComplianceViolations; @@ -198,10 +197,10 @@ public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) @Override public ByteBuffer onUpgradeFrom() { - if (BufferUtil.hasContent(_requestBuffer)) + if (!isRequestBufferEmpty()) { - ByteBuffer unconsumed = ByteBuffer.allocateDirect(_requestBuffer.remaining()); - unconsumed.put(_requestBuffer); + ByteBuffer unconsumed = ByteBuffer.allocateDirect(_retainableByteBuffer.remaining()); + unconsumed.put(_retainableByteBuffer.getBuffer()); unconsumed.flip(); releaseRequestBuffer(); return unconsumed; @@ -225,36 +224,41 @@ public void onFlushed(long bytes) throws IOException void releaseRequestBuffer() { - if (_requestBuffer != null && !_requestBuffer.hasRemaining()) + if (_retainableByteBuffer != null && !_retainableByteBuffer.hasRemaining()) { if (LOG.isDebugEnabled()) LOG.debug("releaseRequestBuffer {}", this); - ByteBuffer buffer = _requestBuffer; - _requestBuffer = null; - _bufferPool.release(buffer); + if (_retainableByteBuffer.release() == 0) + _retainableByteBuffer = null; } } public ByteBuffer getRequestBuffer() { - if (_requestBuffer == null) + if (_retainableByteBuffer == null) { boolean useDirectByteBuffers = isUseInputDirectByteBuffers(); - _requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectByteBuffers); + _retainableByteBuffer = new RetainableByteBuffer(_bufferPool, getInputBufferSize(), useDirectByteBuffers); } - return _requestBuffer; + return _retainableByteBuffer.getBuffer(); } public boolean isRequestBufferEmpty() { - return BufferUtil.isEmpty(_requestBuffer); + return _retainableByteBuffer == null || _retainableByteBuffer.isEmpty(); + } + + private String toDetailString() + { + ByteBuffer buffer = _retainableByteBuffer == null ? null :_retainableByteBuffer.getBuffer(); + return BufferUtil.toDetailString(buffer); } @Override public void onFillable() { if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), toDetailString()); HttpConnection last = setCurrentConnection(this); try @@ -302,7 +306,6 @@ else if (filled < 0) { if (LOG.isDebugEnabled()) LOG.debug("{} caught exception {}", this, _channel.getState(), x); - BufferUtil.clear(_requestBuffer); releaseRequestBuffer(); getEndPoint().close(x); } @@ -310,7 +313,7 @@ else if (filled < 0) { setCurrentConnection(last); if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), toDetailString()); } } @@ -338,22 +341,22 @@ void parseAndFillForContent() private int fillRequestBuffer() { - if (_contentBufferReferences.get() > 0) + if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() > 1) throw new IllegalStateException("fill with unconsumed content on " + this); - if (BufferUtil.isEmpty(_requestBuffer)) + if (isRequestBufferEmpty()) { // Get a buffer // We are not in a race here for the request buffer as we have not yet received a request, // so there are not an possible legal threads calling #parseContent or #completed. - _requestBuffer = getRequestBuffer(); + ByteBuffer requestBuffer = getRequestBuffer(); // fill try { - int filled = getEndPoint().fill(_requestBuffer); + int filled = getEndPoint().fill(requestBuffer); if (filled == 0) // Do a retry on fill 0 (optimization for SSL connections) - filled = getEndPoint().fill(_requestBuffer); + filled = getEndPoint().fill(requestBuffer); if (filled > 0) bytesIn.add(filled); @@ -361,7 +364,7 @@ else if (filled < 0) _parser.atEOF(); if (LOG.isDebugEnabled()) - LOG.debug("{} filled {} {}", this, filled, BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} filled {} {}", this, filled, toDetailString()); return filled; } @@ -379,15 +382,15 @@ else if (filled < 0) private boolean parseRequestBuffer() { if (LOG.isDebugEnabled()) - LOG.debug("{} parse {}", this, BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} parse {}", this, toDetailString()); - boolean handle = _parser.parseNext(_requestBuffer == null ? BufferUtil.EMPTY_BUFFER : _requestBuffer); + boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getBuffer()); if (LOG.isDebugEnabled()) LOG.debug("{} parsed {} {}", this, handle, _parser); // recycle buffer ? - if (_contentBufferReferences.get() == 0) + if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() == 1) releaseRequestBuffer(); return handle; @@ -406,15 +409,14 @@ private boolean upgrade() _channel.recycle(); _parser.reset(); _generator.reset(); - if (_contentBufferReferences.get() == 0) + if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() == 1) { releaseRequestBuffer(); } - else + else if (_retainableByteBuffer != null) { LOG.warn("{} lingering content references?!?!", this); - _requestBuffer = null; // Not returned to pool! - _contentBufferReferences.set(0); + _retainableByteBuffer = null; // Not returned to pool! } return true; } @@ -472,7 +474,7 @@ else if (_generator.isPersistent() && !complete) if (_parser.isStart()) { // if the buffer is empty - if (BufferUtil.isEmpty(_requestBuffer)) + if (isRequestBufferEmpty()) { // look for more data fillInterested(); @@ -629,21 +631,13 @@ private class Content extends HttpInput.Content public Content(ByteBuffer content) { super(content); - _contentBufferReferences.incrementAndGet(); + _retainableByteBuffer.retain(); } @Override public void succeeded() { - int counter = _contentBufferReferences.decrementAndGet(); - if (counter == 0) - releaseRequestBuffer(); - // TODO: this should do something (warn? fail?) if _contentBufferReferences goes below 0 - if (counter < 0) - { - LOG.warn("Content reference counting went below zero: {}", counter); - _contentBufferReferences.incrementAndGet(); - } + _retainableByteBuffer.release(); } @Override From b22c8cb92ee87bb62877148fc922684ec601cc46 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 28 May 2021 12:23:44 +0200 Subject: [PATCH 02/75] fix checkstyle Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/server/HttpConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 1562e15d5f56..2af284742449 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -250,7 +250,7 @@ public boolean isRequestBufferEmpty() private String toDetailString() { - ByteBuffer buffer = _retainableByteBuffer == null ? null :_retainableByteBuffer.getBuffer(); + ByteBuffer buffer = _retainableByteBuffer == null ? null : _retainableByteBuffer.getBuffer(); return BufferUtil.toDetailString(buffer); } From a093959d84d48b08a6f1b76c6222ec2e7ec727d4 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Tue, 1 Jun 2021 10:54:19 +0200 Subject: [PATCH 03/75] replace toDetailString with straight RetainableByteBuffer.toString Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/server/HttpConnection.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 2af284742449..aeba8f15408a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -248,17 +248,11 @@ public boolean isRequestBufferEmpty() return _retainableByteBuffer == null || _retainableByteBuffer.isEmpty(); } - private String toDetailString() - { - ByteBuffer buffer = _retainableByteBuffer == null ? null : _retainableByteBuffer.getBuffer(); - return BufferUtil.toDetailString(buffer); - } - @Override public void onFillable() { if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), toDetailString()); + LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), _retainableByteBuffer); HttpConnection last = setCurrentConnection(this); try @@ -313,7 +307,7 @@ else if (filled < 0) { setCurrentConnection(last); if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), toDetailString()); + LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), _retainableByteBuffer); } } @@ -364,7 +358,7 @@ else if (filled < 0) _parser.atEOF(); if (LOG.isDebugEnabled()) - LOG.debug("{} filled {} {}", this, filled, toDetailString()); + LOG.debug("{} filled {} {}", this, filled, _retainableByteBuffer); return filled; } @@ -382,7 +376,7 @@ else if (filled < 0) private boolean parseRequestBuffer() { if (LOG.isDebugEnabled()) - LOG.debug("{} parse {}", this, toDetailString()); + LOG.debug("{} parse {}", this, _retainableByteBuffer); boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getBuffer()); From 4c5404de42ee3ae302f0b95d60bd959ed78627f4 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Tue, 1 Jun 2021 11:22:27 +0200 Subject: [PATCH 04/75] use RetainableByteBuffer for encrypted input buffer Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/ssl/SslConnection.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 24bc59c19b4e..82a996c7ac70 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -35,6 +35,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -110,7 +111,7 @@ private enum FlushState private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; - private ByteBuffer _encryptedInput; + private RetainableByteBuffer _encryptedInput; private ByteBuffer _encryptedOutput; private final boolean _encryptedDirectBuffers; private final boolean _decryptedDirectBuffers; @@ -326,7 +327,7 @@ private int getBufferSize(ToIntFunction bufferSizeFn) private void acquireEncryptedInput() { if (_encryptedInput == null) - _encryptedInput = _bufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); + _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); } private void acquireEncryptedOutput() @@ -339,7 +340,7 @@ private void acquireEncryptedOutput() public void onUpgradeTo(ByteBuffer buffer) { acquireEncryptedInput(); - BufferUtil.append(_encryptedInput, buffer); + BufferUtil.append(_encryptedInput.getBuffer(), buffer); } @Override @@ -409,7 +410,7 @@ protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuff @Override public String toConnectionString() { - ByteBuffer b = _encryptedInput; + ByteBuffer b = _encryptedInput == null ? null : _encryptedInput.getBuffer(); int ei = b == null ? -1 : b.remaining(); b = _encryptedOutput; int eo = b == null ? -1 : b.remaining(); @@ -431,7 +432,7 @@ private void releaseEncryptedInputBuffer() { if (_encryptedInput != null && !_encryptedInput.hasRemaining()) { - _bufferPool.release(_encryptedInput); + _encryptedInput.release(); _encryptedInput = null; } } @@ -672,14 +673,14 @@ public int fill(ByteBuffer buffer) throws IOException } // Let's try reading some encrypted data... even if we have some already. - int netFilled = networkFill(_encryptedInput); + int netFilled = networkFill(_encryptedInput.getBuffer()); if (netFilled > 0) _bytesIn.addAndGet(netFilled); if (LOG.isDebugEnabled()) LOG.debug("net filled={}", netFilled); // Workaround for Java 11 behavior. - if (netFilled < 0 && isHandshakeInitial() && BufferUtil.isEmpty(_encryptedInput)) + if (netFilled < 0 && isHandshakeInitial() && (_encryptedInput == null || _encryptedInput.isEmpty())) closeInbound(); if (netFilled > 0 && !isHandshakeComplete() && isOutboundDone()) @@ -698,7 +699,7 @@ public int fill(ByteBuffer buffer) throws IOException try { _underflown = false; - unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput, appIn); + unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput.getBuffer(), appIn); } finally { @@ -708,7 +709,7 @@ public int fill(ByteBuffer buffer) throws IOException LOG.debug("unwrap net_filled={} {} encryptedBuffer={} unwrapBuffer={} appBuffer={}", netFilled, StringUtil.replace(unwrapResult.toString(), '\n', ' '), - BufferUtil.toSummaryString(_encryptedInput), + _encryptedInput, BufferUtil.toDetailString(appIn), BufferUtil.toDetailString(buffer)); @@ -729,13 +730,13 @@ public int fill(ByteBuffer buffer) throws IOException case BUFFER_UNDERFLOW: // Continue if we can compact? - if (BufferUtil.compact(_encryptedInput)) + if (BufferUtil.compact(_encryptedInput.getBuffer())) continue; // Are we out of space? - if (BufferUtil.space(_encryptedInput) == 0) + if (BufferUtil.space(_encryptedInput.getBuffer()) == 0) { - BufferUtil.clear(_encryptedInput); + BufferUtil.clear(_encryptedInput.getBuffer()); throw new SSLHandshakeException("Encrypted buffer max length exceeded"); } @@ -847,7 +848,7 @@ protected void needsFillInterest() _flushState, _fillState, _underflown, - BufferUtil.toDetailString(_encryptedInput), + _encryptedInput, BufferUtil.toDetailString(_decryptedInput), SslConnection.this); @@ -855,7 +856,7 @@ protected void needsFillInterest() return; // Fillable if we have decrypted input OR enough encrypted input. - fillable = BufferUtil.hasContent(_decryptedInput) || (BufferUtil.hasContent(_encryptedInput) && !_underflown); + fillable = BufferUtil.hasContent(_decryptedInput) || (_encryptedInput != null && _encryptedInput.hasRemaining() && !_underflown); HandshakeStatus status = _sslEngine.getHandshakeStatus(); switch (status) From 36794d9c86dc14ad4cf4adbba6f31bb8c35deaeb Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 7 Jun 2021 10:50:45 +0200 Subject: [PATCH 05/75] add managed attributes Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/io/AbstractByteBufferPool.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java index db6cb7ed56c0..886d22025508 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java @@ -90,6 +90,18 @@ public long getHeapMemory() return getMemory(false); } + @ManagedAttribute("The maximum bytes retained by direct ByteBuffers") + public long getMaxDirectMemory() + { + return _maxDirectMemory; + } + + @ManagedAttribute("The maximum bytes retained by heap ByteBuffers") + public long getMaxHeapMemory() + { + return _maxHeapMemory; + } + public long getMemory(boolean direct) { AtomicLong memory = direct ? _directMemory : _heapMemory; From 9e58a2abb5d4bd671b198c9d02c1d0c75de5d047 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 7 Jun 2021 12:45:24 +0200 Subject: [PATCH 06/75] add a bit more monitoring to ArrayByteBufferPool Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/ArrayByteBufferPool.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java index 7b37401be5a7..4670b4094d0d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java @@ -14,8 +14,11 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.LongAdder; import java.util.function.IntFunction; import org.eclipse.jetty.util.BufferUtil; @@ -38,6 +41,8 @@ public class ArrayByteBufferPool extends AbstractByteBufferPool private final int _minCapacity; private final ByteBufferPool.Bucket[] _direct; private final ByteBufferPool.Bucket[] _indirect; + private final LongAdder _newDirectByteBuffersCount = new LongAdder(); + private final LongAdder _newIndirectByteBuffersCount = new LongAdder(); /** * Creates a new ArrayByteBufferPool with a default configuration. @@ -100,6 +105,52 @@ public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int max _indirect = new ByteBufferPool.Bucket[length]; } + @Override + public ByteBuffer newByteBuffer(int capacity, boolean direct) + { + if (direct) + _newDirectByteBuffersCount.increment(); + else + _newIndirectByteBuffersCount.increment(); + return super.newByteBuffer(capacity, direct); + } + + @ManagedAttribute("The number of times a new direct byte buffer got allocated") + public long getNewDirectByteBuffersCount() + { + return _newDirectByteBuffersCount.sum(); + } + + @ManagedAttribute("The number of times a new indirect byte buffer got allocated") + public long getNewIndirectByteBuffersCount() + { + return _newIndirectByteBuffersCount.sum(); + } + + @ManagedAttribute("direct buffers buckets") + public List getDirect() + { + List set = new ArrayList<>(); + for (Bucket bucket : _direct) + { + if (bucket != null) + set.add(bucket.toString()); + } + return set; + } + + @ManagedAttribute("indirect buffers buckets") + public List getIndirect() + { + List set = new ArrayList<>(); + for (Bucket bucket : _indirect) + { + if (bucket != null) + set.add(bucket.toString()); + } + return set; + } + @Override public ByteBuffer acquire(int size, boolean direct) { From ea19cae890bf5e51bfa9f5590dfdfc9cfc98949e Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 7 Jun 2021 17:25:06 +0200 Subject: [PATCH 07/75] modify SslConnectionFactory to add ability to use Pool for encrypted input Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBuffer.java | 20 +++++++++--- .../eclipse/jetty/io/ssl/SslConnection.java | 32 ++++++++++++++++++- .../jetty/server/SslConnectionFactory.java | 4 ++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 1ab6d1ffb2ed..62b02357cf1a 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -39,8 +39,17 @@ public RetainableByteBuffer(ByteBufferPool pool, int size) public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) { this.pool = pool; - this.buffer = pool.acquire(size, direct); - this.references = new AtomicInteger(1); + if (pool != null) + { + this.buffer = pool.acquire(size, direct); + this.references = new AtomicInteger(1); + } + else + { + this.buffer = direct ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); + BufferUtil.clear(this.buffer); + this.references = new AtomicInteger(0); + } } public ByteBuffer getBuffer() @@ -59,7 +68,7 @@ public void retain() while (true) { int r = references.get(); - if (r == 0) + if (r == 0 && pool != null) throw new IllegalStateException("released " + this); if (references.compareAndSet(r, r + 1)) break; @@ -70,7 +79,10 @@ public int release() { int ref = references.decrementAndGet(); if (ref == 0) - pool.release(buffer); + if (pool != null) + pool.release(buffer); + else + BufferUtil.clear(buffer); else if (ref < 0) throw new IllegalStateException("already released " + this); return ref; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 82a996c7ac70..a4854d8ed7f9 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.Invocable; @@ -108,10 +109,12 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; + private final Pool _retainableBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; private RetainableByteBuffer _encryptedInput; + private Pool.Entry _encryptedInputEntry; private ByteBuffer _encryptedOutput; private final boolean _encryptedDirectBuffers; private final boolean _decryptedDirectBuffers; @@ -188,11 +191,18 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) + { + this(null, byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + } + + public SslConnection(Pool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. // onFillable() does not block and will only wakeup another thread to do the actual reading and handling. super(endPoint, executor); this._bufferPool = byteBufferPool; + this._retainableBufferPool = retainableByteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = newDecryptedEndPoint(); this._encryptedDirectBuffers = useDirectBuffersForEncryption; @@ -327,7 +337,22 @@ private int getBufferSize(ToIntFunction bufferSizeFn) private void acquireEncryptedInput() { if (_encryptedInput == null) - _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); + { + if (_retainableBufferPool == null) + _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); + else + { + _encryptedInputEntry = _retainableBufferPool.acquire(); + if (_encryptedInputEntry == null) + { + LOG.warn("pool is depleted"); + _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); + return; + } + _encryptedInput = _encryptedInputEntry.getPooled(); + _encryptedInput.retain(); + } + } } private void acquireEncryptedOutput() @@ -433,6 +458,11 @@ private void releaseEncryptedInputBuffer() if (_encryptedInput != null && !_encryptedInput.hasRemaining()) { _encryptedInput.release(); + if (_retainableBufferPool != null) + { + _retainableBufferPool.release(_encryptedInputEntry); + _encryptedInputEntry = null; + } _encryptedInput = null; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index e97d82f442b6..90b1192aea36 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; +import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -160,7 +161,8 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + Pool pool = connector.getBean(Pool.class); + return new SslConnection(pool, connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override From 2ed875c7d7d2064319aff71556b33b58e768395a Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:08:47 +0200 Subject: [PATCH 08/75] remove unneeded counters Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/ArrayByteBufferPool.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java index 4670b4094d0d..bb09b2be2976 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java @@ -41,8 +41,6 @@ public class ArrayByteBufferPool extends AbstractByteBufferPool private final int _minCapacity; private final ByteBufferPool.Bucket[] _direct; private final ByteBufferPool.Bucket[] _indirect; - private final LongAdder _newDirectByteBuffersCount = new LongAdder(); - private final LongAdder _newIndirectByteBuffersCount = new LongAdder(); /** * Creates a new ArrayByteBufferPool with a default configuration. @@ -105,28 +103,6 @@ public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int max _indirect = new ByteBufferPool.Bucket[length]; } - @Override - public ByteBuffer newByteBuffer(int capacity, boolean direct) - { - if (direct) - _newDirectByteBuffersCount.increment(); - else - _newIndirectByteBuffersCount.increment(); - return super.newByteBuffer(capacity, direct); - } - - @ManagedAttribute("The number of times a new direct byte buffer got allocated") - public long getNewDirectByteBuffersCount() - { - return _newDirectByteBuffersCount.sum(); - } - - @ManagedAttribute("The number of times a new indirect byte buffer got allocated") - public long getNewIndirectByteBuffersCount() - { - return _newIndirectByteBuffersCount.sum(); - } - @ManagedAttribute("direct buffers buckets") public List getDirect() { From 2e2a90c88b9f93e75e2f88d3a4d4b12e69d8d8e6 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:09:32 +0200 Subject: [PATCH 09/75] first shot at RetainableByteBuffer pool Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBuffer.java | 26 ++- .../jetty/io/RetainableByteBufferPool.java | 178 ++++++++++++++++++ .../io/RetainableByteBufferPoolTest.java | 59 ++++++ 3 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java create mode 100644 jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 62b02357cf1a..02dd30d6fa97 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -16,6 +16,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Retainable; @@ -25,11 +26,12 @@ * initially 1, incremented with {@link #retain()} and decremented with {@link #release()}. The buffer * is released to the pool when the reference count is decremented to 0.

*/ -public class RetainableByteBuffer implements Retainable +public class RetainableByteBuffer implements Retainable, Attachable { private final ByteBufferPool pool; private final ByteBuffer buffer; private final AtomicInteger references; + private Object attachment; public RetainableByteBuffer(ByteBufferPool pool, int size) { @@ -52,6 +54,23 @@ public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) } } + public int capacity() + { + return buffer.capacity(); + } + + @Override + public Object getAttachment() + { + return attachment; + } + + @Override + public void setAttachment(Object attachment) + { + this.attachment = attachment; + } + public ByteBuffer getBuffer() { return buffer; @@ -62,6 +81,11 @@ public int getReferences() return references.get(); } + public boolean isDirect() + { + return buffer.isDirect(); + } + @Override public void retain() { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java new file mode 100644 index 000000000000..f2a277d93c95 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -0,0 +1,178 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Supplier; + +import org.eclipse.jetty.util.Pool; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ManagedObject +public class RetainableByteBufferPool +{ + private static final Logger LOG = LoggerFactory.getLogger(RetainableByteBufferPool.class); + + private final Pool[] _direct; + private final Pool[] _indirect; + private final int _factor; + private final int _maxQueueLength; + private final int _minCapacity; + + public RetainableByteBufferPool() + { + this(1024, 1024, 65536, Integer.MAX_VALUE); + } + + public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxQueueLength) + { + _factor = factor <= 0 ? 1024 : factor; + _maxQueueLength = maxQueueLength; + if (minCapacity <= 0) + minCapacity = 0; + _minCapacity = minCapacity; + if (maxCapacity <= 0) + maxCapacity = 64 * 1024; + if ((maxCapacity % _factor) != 0 || _factor >= maxCapacity) + throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); + + int length = maxCapacity / _factor; + + @SuppressWarnings("unchecked") + Pool[] directArray = new Pool[length]; + _direct = directArray; + @SuppressWarnings("unchecked") + Pool[] indirectArray = new Pool[length]; + _indirect = indirectArray; + } + + public RetainableByteBuffer acquire(int size, boolean direct) + { + int capacity = (bucketFor(size) + 1) * _factor; + Pool bucket = bucketFor(capacity, direct, null); + if (bucket == null) + return newRetainableByteBuffer(capacity, direct); + Pool.Entry entry = bucket.acquire(); + if (entry == null) + return newRetainableByteBuffer(capacity, direct); + RetainableByteBuffer buffer = entry.getPooled(); + buffer.retain(); + return buffer; + } + + public void release(RetainableByteBuffer buffer) + { + if (buffer == null) + return; + + int capacity = buffer.capacity(); + // Validate that this buffer is from this pool. + if ((capacity % _factor) != 0) + { + if (LOG.isDebugEnabled()) + LOG.debug("RetainableByteBuffer {} does not belong to this pool, discarding it", buffer); + return; + } + + Object attachment = buffer.getAttachment(); + if (attachment != null && !(attachment instanceof Pool.Entry)) + { + if (LOG.isDebugEnabled()) + LOG.debug("RetainableByteBuffer {} does not belong to this pool, discarding it", buffer); + return; + } + + buffer.release(); + + @SuppressWarnings("unchecked") + Pool.Entry entry = (Pool.Entry)attachment; + if (entry != null) + { + entry.release(); + } + else + { + Pool bucket = bucketFor(capacity, buffer.isDirect(), this::newBucket); + if (bucket != null) + { + Pool.Entry reservedEntry = bucket.reserve(); + if (reservedEntry != null) + { + buffer.setAttachment(reservedEntry); + reservedEntry.enable(buffer, false); + } + } + } + } + + private Pool bucketFor(int capacity, boolean direct, Supplier> newBucket) + { + if (capacity < _minCapacity) + return null; + int b = bucketFor(capacity); + if (b >= _direct.length) + return null; + Pool[] buckets = bucketsFor(direct); + Pool bucket = buckets[b]; + if (bucket == null && newBucket != null) + buckets[b] = bucket = newBucket.get(); + return bucket; + } + + private int bucketFor(int capacity) + { + return (capacity - 1) / _factor; + } + + private Pool[] bucketsFor(boolean direct) + { + return direct ? _direct : _indirect; + } + + private Pool newBucket() + { + return new Pool<>(Pool.StrategyType.THREAD_ID, _maxQueueLength); + } + + private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direct) + { + RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(null, capacity, direct); + retainableByteBuffer.retain(); + return retainableByteBuffer; + } + + @ManagedAttribute("The number of pooled direct ByteBuffers") + public long getDirectByteBufferCount() + { + return getByteBufferCount(true); + } + + @ManagedAttribute("The number of pooled heap ByteBuffers") + public long getHeapByteBufferCount() + { + return getByteBufferCount(false); + } + + private long getByteBufferCount(boolean direct) + { + return Arrays.stream(bucketsFor(direct)) + .filter(Objects::nonNull) + .mapToLong(Pool::size) + .sum(); + } +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java new file mode 100644 index 000000000000..12ff9f04490c --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java @@ -0,0 +1,59 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; + +public class RetainableByteBufferPoolTest +{ + @Test + public void testAcquireRelease() + { + RetainableByteBufferPool pool = new RetainableByteBufferPool(); + + for (int i = 0; i < 10; i++) + { + { + RetainableByteBuffer buffer = pool.acquire(10, true); + assertThat(buffer, is(notNullValue())); + RetainableByteBuffer buffer2 = pool.acquire(10, true); + assertThat(buffer2, is(notNullValue())); + pool.release(buffer); + pool.release(buffer2); + } + { + RetainableByteBuffer buffer = pool.acquire(16385, true); + assertThat(buffer, is(notNullValue())); + pool.release(buffer); + } + { + RetainableByteBuffer buffer = pool.acquire(32768, true); + assertThat(buffer, is(notNullValue())); + pool.release(buffer); + } + { + RetainableByteBuffer buffer = pool.acquire(32768, false); + assertThat(buffer, is(notNullValue())); + pool.release(buffer); + } + } + + assertThat(pool.getDirectByteBufferCount(), is(4L)); + assertThat(pool.getHeapByteBufferCount(), is(1L)); + } +} From 45a9b598f5bd91bd0bbdd7e3bc5b1ff92487c0ac Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:31:55 +0200 Subject: [PATCH 10/75] cleanup acquire/retain mechanism Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBuffer.java | 23 +++--- .../jetty/io/RetainableByteBufferPool.java | 82 ++++++------------- 2 files changed, 36 insertions(+), 69 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 02dd30d6fa97..a8e23815b01c 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -18,6 +18,7 @@ import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.Retainable; /** @@ -31,12 +32,7 @@ public class RetainableByteBuffer implements Retainable, Attachable private final ByteBufferPool pool; private final ByteBuffer buffer; private final AtomicInteger references; - private Object attachment; - - public RetainableByteBuffer(ByteBufferPool pool, int size) - { - this(pool, size, false); - } + private Pool.Entry entry; public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) { @@ -44,14 +40,13 @@ public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) if (pool != null) { this.buffer = pool.acquire(size, direct); - this.references = new AtomicInteger(1); } else { this.buffer = direct ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); BufferUtil.clear(this.buffer); - this.references = new AtomicInteger(0); } + this.references = new AtomicInteger(1); } public int capacity() @@ -62,13 +57,15 @@ public int capacity() @Override public Object getAttachment() { - return attachment; + return entry; } @Override public void setAttachment(Object attachment) { - this.attachment = attachment; + @SuppressWarnings("unchecked") + Pool.Entry entry = (Pool.Entry)attachment; + this.entry = entry; } public ByteBuffer getBuffer() @@ -104,9 +101,15 @@ public int release() int ref = references.decrementAndGet(); if (ref == 0) if (pool != null) + { pool.release(buffer); + } else + { BufferUtil.clear(buffer); + if (entry != null) + entry.release(); + } else if (ref < 0) throw new IllegalStateException("already released " + this); return ref; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index f2a277d93c95..059b9a31322e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -20,29 +20,25 @@ import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @ManagedObject public class RetainableByteBufferPool { - private static final Logger LOG = LoggerFactory.getLogger(RetainableByteBufferPool.class); - private final Pool[] _direct; private final Pool[] _indirect; private final int _factor; - private final int _maxQueueLength; + private final int _maxBucketSize; private final int _minCapacity; public RetainableByteBufferPool() { - this(1024, 1024, 65536, Integer.MAX_VALUE); + this(0, 1024, 65536, Integer.MAX_VALUE); } - public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxQueueLength) + public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) { _factor = factor <= 0 ? 1024 : factor; - _maxQueueLength = maxQueueLength; + _maxBucketSize = maxBucketSize; if (minCapacity <= 0) minCapacity = 0; _minCapacity = minCapacity; @@ -64,14 +60,27 @@ public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, in public RetainableByteBuffer acquire(int size, boolean direct) { int capacity = (bucketFor(size) + 1) * _factor; - Pool bucket = bucketFor(capacity, direct, null); + Pool bucket = bucketFor(size, direct, this::newBucket); if (bucket == null) - return newRetainableByteBuffer(capacity, direct); + return new RetainableByteBuffer(null, capacity, direct); Pool.Entry entry = bucket.acquire(); + + RetainableByteBuffer buffer; if (entry == null) - return newRetainableByteBuffer(capacity, direct); - RetainableByteBuffer buffer = entry.getPooled(); - buffer.retain(); + { + buffer = new RetainableByteBuffer(null, capacity, direct); + Pool.Entry reservedEntry = bucket.reserve(); + if (reservedEntry != null) + { + buffer.setAttachment(reservedEntry); + reservedEntry.enable(buffer, true); + } + } + else + { + buffer = entry.getPooled(); + buffer.retain(); + } return buffer; } @@ -79,45 +88,7 @@ public void release(RetainableByteBuffer buffer) { if (buffer == null) return; - - int capacity = buffer.capacity(); - // Validate that this buffer is from this pool. - if ((capacity % _factor) != 0) - { - if (LOG.isDebugEnabled()) - LOG.debug("RetainableByteBuffer {} does not belong to this pool, discarding it", buffer); - return; - } - - Object attachment = buffer.getAttachment(); - if (attachment != null && !(attachment instanceof Pool.Entry)) - { - if (LOG.isDebugEnabled()) - LOG.debug("RetainableByteBuffer {} does not belong to this pool, discarding it", buffer); - return; - } - buffer.release(); - - @SuppressWarnings("unchecked") - Pool.Entry entry = (Pool.Entry)attachment; - if (entry != null) - { - entry.release(); - } - else - { - Pool bucket = bucketFor(capacity, buffer.isDirect(), this::newBucket); - if (bucket != null) - { - Pool.Entry reservedEntry = bucket.reserve(); - if (reservedEntry != null) - { - buffer.setAttachment(reservedEntry); - reservedEntry.enable(buffer, false); - } - } - } } private Pool bucketFor(int capacity, boolean direct, Supplier> newBucket) @@ -146,14 +117,7 @@ private Pool[] bucketsFor(boolean direct) private Pool newBucket() { - return new Pool<>(Pool.StrategyType.THREAD_ID, _maxQueueLength); - } - - private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direct) - { - RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(null, capacity, direct); - retainableByteBuffer.retain(); - return retainableByteBuffer; + return new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize); } @ManagedAttribute("The number of pooled direct ByteBuffers") From 2ad1c63b32e4d689e51c39a22296e6c45ee74d9e Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:33:21 +0200 Subject: [PATCH 11/75] use pool-specific ctor Signed-off-by: Ludovic Orban --- .../main/java/org/eclipse/jetty/io/RetainableByteBuffer.java | 5 +++++ .../java/org/eclipse/jetty/io/RetainableByteBufferPool.java | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index a8e23815b01c..29048f65abb6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -34,6 +34,11 @@ public class RetainableByteBuffer implements Retainable, Attachable private final AtomicInteger references; private Pool.Entry entry; + RetainableByteBuffer(int size, boolean direct) + { + this(null, size, direct); + } + public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) { this.pool = pool; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 059b9a31322e..7e2f0a02dff7 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -62,13 +62,13 @@ public RetainableByteBuffer acquire(int size, boolean direct) int capacity = (bucketFor(size) + 1) * _factor; Pool bucket = bucketFor(size, direct, this::newBucket); if (bucket == null) - return new RetainableByteBuffer(null, capacity, direct); + return new RetainableByteBuffer(capacity, direct); Pool.Entry entry = bucket.acquire(); RetainableByteBuffer buffer; if (entry == null) { - buffer = new RetainableByteBuffer(null, capacity, direct); + buffer = new RetainableByteBuffer(capacity, direct); Pool.Entry reservedEntry = bucket.reserve(); if (reservedEntry != null) { From 8eb2284dc901f3a9f0d8ce303192e5ad99a0cfbc Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:53:10 +0200 Subject: [PATCH 12/75] add optional ability to use RetainableByteBufferPool on the server Signed-off-by: Ludovic Orban --- .../eclipse/jetty/http2/HTTP2Connection.java | 10 +++++- .../eclipse/jetty/io/ssl/SslConnection.java | 24 +++---------- .../eclipse/jetty/server/HttpConnection.java | 9 +++-- .../core/internal/WebSocketConnection.java | 35 +++++++++++++++++-- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 5b9a7b691fb5..9e792794fb85 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -50,6 +51,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final HTTP2Producer producer = new HTTP2Producer(); private final AtomicLong bytesIn = new AtomicLong(); private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Parser parser; private final ISession session; private final int bufferSize; @@ -58,9 +60,15 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private boolean useOutputDirectByteBuffers; public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + { + this(byteBufferPool, null, executor, endPoint, parser, session, bufferSize); + } + + public HTTP2Connection(ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); this.byteBufferPool = byteBufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.parser = parser; this.session = session; this.bufferSize = bufferSize; @@ -425,7 +433,7 @@ private class NetworkBuffer extends RetainableByteBuffer implements Callback { private NetworkBuffer() { - super(byteBufferPool, bufferSize, isUseInputDirectByteBuffers()); + super(retainableByteBufferPool == null ? byteBufferPool : null, bufferSize, isUseInputDirectByteBuffers()); } private void put(ByteBuffer source) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index a4854d8ed7f9..472d44c596d1 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -36,10 +36,10 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; -import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.thread.AutoLock; import org.eclipse.jetty.util.thread.Invocable; @@ -109,12 +109,11 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; - private final Pool _retainableBufferPool; + private final RetainableByteBufferPool _retainableBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; private RetainableByteBuffer _encryptedInput; - private Pool.Entry _encryptedInputEntry; private ByteBuffer _encryptedOutput; private final boolean _encryptedDirectBuffers; private final boolean _decryptedDirectBuffers; @@ -195,7 +194,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint this(null, byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } - public SslConnection(Pool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. @@ -341,17 +340,7 @@ private void acquireEncryptedInput() if (_retainableBufferPool == null) _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); else - { - _encryptedInputEntry = _retainableBufferPool.acquire(); - if (_encryptedInputEntry == null) - { - LOG.warn("pool is depleted"); - _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); - return; - } - _encryptedInput = _encryptedInputEntry.getPooled(); - _encryptedInput.retain(); - } + _encryptedInput = _retainableBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); } } @@ -458,11 +447,6 @@ private void releaseEncryptedInputBuffer() if (_encryptedInput != null && !_encryptedInput.hasRemaining()) { _encryptedInput.release(); - if (_retainableBufferPool != null) - { - _retainableBufferPool.release(_encryptedInputEntry); - _encryptedInputEntry = null; - } _encryptedInput = null; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index aeba8f15408a..661b96f570d2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -35,6 +35,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -56,6 +57,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpConfiguration _config; private final Connector _connector; private final ByteBufferPool _bufferPool; + private final RetainableByteBufferPool _retainableBufferPool; private final HttpInput _input; private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; @@ -95,6 +97,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); + _retainableBufferPool = connector.getBean(RetainableByteBufferPool.class); _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); @@ -237,8 +240,10 @@ public ByteBuffer getRequestBuffer() { if (_retainableByteBuffer == null) { - boolean useDirectByteBuffers = isUseInputDirectByteBuffers(); - _retainableByteBuffer = new RetainableByteBuffer(_bufferPool, getInputBufferSize(), useDirectByteBuffers); + if (_retainableBufferPool == null) + _retainableByteBuffer = new RetainableByteBuffer(_bufferPool, getInputBufferSize(), isUseInputDirectByteBuffers()); + else + _retainableByteBuffer = _retainableBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); } return _retainableByteBuffer.getBuffer(); } diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 927c7a763a1f..dde000d362f3 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.component.Dumpable; @@ -52,6 +53,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private final AutoLock lock = new AutoLock(); private final ByteBufferPool bufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Generator generator; private final Parser parser; private final WebSocketCoreSession coreSession; @@ -79,7 +81,7 @@ public WebSocketConnection(EndPoint endp, ByteBufferPool bufferPool, WebSocketCoreSession coreSession) { - this(endp, executor, scheduler, bufferPool, coreSession, null); + this(endp, executor, scheduler, bufferPool, null, coreSession, null); } /** @@ -101,6 +103,31 @@ public WebSocketConnection(EndPoint endp, ByteBufferPool bufferPool, WebSocketCoreSession coreSession, Random randomMask) + { + this(endp, executor, scheduler, bufferPool, null, coreSession, randomMask); + } + + /** + * Create a WSConnection. + *

+ * It is assumed that the WebSocket Upgrade Handshake has already + * completed successfully before creating this connection. + *

+ * @param endp The endpoint ever which Websockot is sent/received + * @param executor A thread executor to use for WS callbacks. + * @param scheduler A scheduler to use for timeouts + * @param bufferPool A pool of buffers to use. + * @param retainableByteBufferPool A pool of retainable buffers to use. + * @param coreSession The WC core session to which frames are delivered. + * @param randomMask A Random used to mask frames. If null then SecureRandom will be created if needed. + */ + public WebSocketConnection(EndPoint endp, + Executor executor, + Scheduler scheduler, + ByteBufferPool bufferPool, + RetainableByteBufferPool retainableByteBufferPool, + WebSocketCoreSession coreSession, + Random randomMask) { super(endp, executor); @@ -110,6 +137,7 @@ public WebSocketConnection(EndPoint endp, Objects.requireNonNull(bufferPool, "ByteBufferPool"); this.bufferPool = bufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.coreSession = coreSession; this.generator = new Generator(); this.parser = new Parser(bufferPool, coreSession); @@ -283,7 +311,10 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer(int capacity) { - return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers()); + if (retainableByteBufferPool == null) + return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers()); + else + return retainableByteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); } private void releaseNetworkBuffer() From 9b2001d0d7ed97c14fe228de0a6b0689de13acd8 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 09:57:30 +0200 Subject: [PATCH 13/75] revert old ByteBufferPool changes Signed-off-by: Ludovic Orban --- .../jetty/io/AbstractByteBufferPool.java | 12 --------- .../eclipse/jetty/io/ArrayByteBufferPool.java | 27 ------------------- 2 files changed, 39 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java index 886d22025508..db6cb7ed56c0 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AbstractByteBufferPool.java @@ -90,18 +90,6 @@ public long getHeapMemory() return getMemory(false); } - @ManagedAttribute("The maximum bytes retained by direct ByteBuffers") - public long getMaxDirectMemory() - { - return _maxDirectMemory; - } - - @ManagedAttribute("The maximum bytes retained by heap ByteBuffers") - public long getMaxHeapMemory() - { - return _maxHeapMemory; - } - public long getMemory(boolean direct) { AtomicLong memory = direct ? _directMemory : _heapMemory; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java index bb09b2be2976..7b37401be5a7 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java @@ -14,11 +14,8 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Objects; -import java.util.concurrent.atomic.LongAdder; import java.util.function.IntFunction; import org.eclipse.jetty.util.BufferUtil; @@ -103,30 +100,6 @@ public ArrayByteBufferPool(int minCapacity, int factor, int maxCapacity, int max _indirect = new ByteBufferPool.Bucket[length]; } - @ManagedAttribute("direct buffers buckets") - public List getDirect() - { - List set = new ArrayList<>(); - for (Bucket bucket : _direct) - { - if (bucket != null) - set.add(bucket.toString()); - } - return set; - } - - @ManagedAttribute("indirect buffers buckets") - public List getIndirect() - { - List set = new ArrayList<>(); - for (Bucket bucket : _indirect) - { - if (bucket != null) - set.add(bucket.toString()); - } - return set; - } - @Override public ByteBuffer acquire(int size, boolean direct) { From 914a932794458dea3d716d4c8a5d9da4c78b27d5 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 10:22:16 +0200 Subject: [PATCH 14/75] fix compilation error Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/server/SslConnectionFactory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 90b1192aea36..6b523ccf58fe 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -21,9 +21,9 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; -import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; @@ -161,8 +161,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - Pool pool = connector.getBean(Pool.class); - return new SslConnection(pool, connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + return new SslConnection(connector.getBean(RetainableByteBufferPool.class), connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override From d2ead6563937b1519a5b001ace60ca5cbb3c5af3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 10:22:52 +0200 Subject: [PATCH 15/75] register RetainableByteBufferPool bean in AbstractConnector Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/server/AbstractConnector.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 7903f7f65ab5..eb3ffeccce31 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.StringUtil; @@ -145,6 +146,7 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co private final Executor _executor; private final Scheduler _scheduler; private final ByteBufferPool _byteBufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final Thread[] _acceptors; private final Set _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set _immutableEndPoints = Collections.unmodifiableSet(_endpoints); @@ -188,6 +190,9 @@ public AbstractConnector( pool = _server.getBean(ByteBufferPool.class); _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); + _retainableByteBufferPool = retainableByteBufferPool != null ? retainableByteBufferPool : new RetainableByteBufferPool(); + addBean(_retainableByteBufferPool); addEventListener(new Container.Listener() { From 01b86c03801bfedf07967af49786bd4aff8905c2 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 10:37:50 +0200 Subject: [PATCH 16/75] add RetainableByteBufferPool registration use in websocket Signed-off-by: Ludovic Orban --- .../websocket/core/internal/WebSocketConnection.java | 12 +++--------- .../core/server/internal/AbstractHandshaker.java | 5 +++-- .../core/server/internal/RFC6455Handshaker.java | 3 ++- .../core/server/internal/RFC8441Handshaker.java | 3 ++- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index dde000d362f3..88bfcccfb0b7 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -90,21 +90,15 @@ public WebSocketConnection(EndPoint endp, * It is assumed that the WebSocket Upgrade Handshake has already * completed successfully before creating this connection. *

- * @param endp The endpoint ever which Websockot is sent/received - * @param executor A thread executor to use for WS callbacks. - * @param scheduler A scheduler to use for timeouts - * @param bufferPool A pool of buffers to use. - * @param coreSession The WC core session to which frames are delivered. - * @param randomMask A Random used to mask frames. If null then SecureRandom will be created if needed. */ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, - WebSocketCoreSession coreSession, - Random randomMask) + RetainableByteBufferPool retainableByteBufferPool, + WebSocketCoreSession coreSession) { - this(endp, executor, scheduler, bufferPool, null, coreSession, randomMask); + this(endp, executor, scheduler, bufferPool, retainableByteBufferPool, coreSession, null); } /** diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index a28c98983f61..734091f247e9 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpTransport; @@ -216,9 +217,9 @@ protected void handle(Runnable runnable) protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession); - protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, WebSocketCoreSession coreSession) + protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { - return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, coreSession); + return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, retainableByteBufferPool, coreSession); } protected abstract void prepareResponse(Response response, WebSocketNegotiation negotiation); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index 041b19e92525..ee09b8660e5b 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -94,7 +95,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web { HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); - return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession); + return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), connector.getBean(RetainableByteBufferPool.class), coreSession); } @Override diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index 05892c948dc8..af080fead85d 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -20,6 +20,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -78,7 +79,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); - return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession); + return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), connector.getBean(RetainableByteBufferPool.class), coreSession); } @Override From dac025c7b2a8f08e2a84f43b503b264f72d4556d Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 10:38:12 +0200 Subject: [PATCH 17/75] add RetainableByteBufferPool registration use in client Signed-off-by: Ludovic Orban --- .../jetty/client/http/HttpReceiverOverHTTP.java | 13 +++++++++++-- .../fcgi/client/http/HttpConnectionOverFCGI.java | 13 +++++++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 04b4843775d5..78b24bfbb992 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; @@ -111,9 +112,17 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer() { HttpClient client = getHttpDestination().getHttpClient(); - ByteBufferPool bufferPool = client.getByteBufferPool(); boolean direct = client.isUseInputDirectByteBuffers(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), direct); + RetainableByteBufferPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + { + ByteBufferPool bufferPool = client.getByteBufferPool(); + return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), direct); + } + else + { + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), direct); + } } private void releaseNetworkBuffer() diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index 19ee20518dc5..1516c85056ee 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -47,6 +47,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -135,8 +136,16 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer() { HttpClient client = destination.getHttpClient(); - ByteBufferPool bufferPool = client.getByteBufferPool(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); + RetainableByteBufferPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + { + ByteBufferPool bufferPool = client.getByteBufferPool(); + return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); + } + else + { + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); + } } private void releaseNetworkBuffer() From 114418ebe23d13eef46f5ed398cd6d82462de8fe Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 10:43:55 +0200 Subject: [PATCH 18/75] add RetainableByteBufferPool registration use in upgrade Signed-off-by: Ludovic Orban --- .../core/client/CoreClientUpgradeRequest.java | 4 +++- .../core/internal/WebSocketConnection.java | 16 ---------------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index c665c7240dcb..d30ac0bad0f2 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -447,7 +448,8 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, coreSession); + RetainableByteBufferPool retainableByteBufferPool = wsClient.getWebSocketComponents().getBean(RetainableByteBufferPool.class); + WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); Exception listenerError = notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(this, response)); diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 88bfcccfb0b7..2fa708160409 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -68,22 +68,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private boolean useInputDirectByteBuffers; private boolean useOutputDirectByteBuffers; - /** - * Create a WSConnection. - *

- * It is assumed that the WebSocket Upgrade Handshake has already - * completed successfully before creating this connection. - *

- */ - public WebSocketConnection(EndPoint endp, - Executor executor, - Scheduler scheduler, - ByteBufferPool bufferPool, - WebSocketCoreSession coreSession) - { - this(endp, executor, scheduler, bufferPool, null, coreSession, null); - } - /** * Create a WSConnection. *

From 8efd144d51a8366ab835b5dac9c375d4ad13fe31 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 11:09:43 +0200 Subject: [PATCH 19/75] make RetainableByteBufferPool never null Signed-off-by: Ludovic Orban --- .../client/http/HttpReceiverOverHTTP.java | 19 ++++++++------- .../client/http/HttpConnectionOverFCGI.java | 20 ++++++++-------- .../eclipse/jetty/http2/HTTP2Connection.java | 5 ++-- .../eclipse/jetty/io/AdapterMemoryPool.java | 23 +++++++++++++++++++ .../org/eclipse/jetty/io/ByteBufferPool.java | 2 +- .../java/org/eclipse/jetty/io/MemoryPool.java | 21 +++++++++++++++++ .../jetty/io/RetainableByteBufferPool.java | 4 +++- .../eclipse/jetty/io/ssl/SslConnection.java | 15 ++++++------ .../jetty/server/AbstractConnector.java | 5 ++-- .../eclipse/jetty/server/HttpConnection.java | 16 ++++++------- .../jetty/server/SslConnectionFactory.java | 10 +++++++- .../core/client/CoreClientUpgradeRequest.java | 7 +++++- .../core/internal/WebSocketConnection.java | 9 ++++---- .../server/internal/AbstractHandshaker.java | 5 ++-- .../server/internal/RFC6455Handshaker.java | 10 +++++++- .../server/internal/RFC8441Handshaker.java | 10 +++++++- 16 files changed, 128 insertions(+), 53 deletions(-) create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 78b24bfbb992..7e05d2b4fbca 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -29,8 +29,10 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; @@ -44,6 +46,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private final LongAdder inMessages = new LongAdder(); private final HttpParser parser; + private final MemoryPool retainableByteBufferPool; private RetainableByteBuffer networkBuffer; private boolean shutdown; private boolean complete; @@ -62,6 +65,11 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheSize(httpTransport.getHeaderCacheSize()); parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } + + MemoryPool retainableByteBufferPool = httpClient.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(httpClient.getByteBufferPool()); + this.retainableByteBufferPool = retainableByteBufferPool; } @Override @@ -113,16 +121,7 @@ private RetainableByteBuffer newNetworkBuffer() { HttpClient client = getHttpDestination().getHttpClient(); boolean direct = client.isUseInputDirectByteBuffers(); - RetainableByteBufferPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - { - ByteBufferPool bufferPool = client.getByteBufferPool(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), direct); - } - else - { - return retainableByteBufferPool.acquire(client.getResponseBufferSize(), direct); - } + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), direct); } private void releaseNetworkBuffer() diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index 1516c85056ee..efa800b6cd2d 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -44,8 +44,10 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; @@ -72,6 +74,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne private final ClientParser parser; private RetainableByteBuffer networkBuffer; private Object attachment; + private final MemoryPool retainableByteBufferPool; public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Promise promise) { @@ -82,6 +85,12 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr this.delegate = new Delegate(destination); this.parser = new ClientParser(new ResponseListener()); requests.addLast(0); + + HttpClient client = destination.getHttpClient(); + MemoryPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(client.getByteBufferPool()); + this.retainableByteBufferPool = retainableByteBufferPool; } public HttpDestination getHttpDestination() @@ -136,16 +145,7 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer() { HttpClient client = destination.getHttpClient(); - RetainableByteBufferPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - { - ByteBufferPool bufferPool = client.getByteBufferPool(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); - } - else - { - return retainableByteBufferPool.acquire(client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); - } + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); } private void releaseNetworkBuffer() diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 9e792794fb85..b2b62a4499ee 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; @@ -51,7 +52,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final HTTP2Producer producer = new HTTP2Producer(); private final AtomicLong bytesIn = new AtomicLong(); private final ByteBufferPool byteBufferPool; - private final RetainableByteBufferPool retainableByteBufferPool; + private final MemoryPool retainableByteBufferPool; private final Parser parser; private final ISession session; private final int bufferSize; @@ -64,7 +65,7 @@ public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoin this(byteBufferPool, null, executor, endPoint, parser, session, bufferSize); } - public HTTP2Connection(ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + public HTTP2Connection(ByteBufferPool byteBufferPool, MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); this.byteBufferPool = byteBufferPool; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java new file mode 100644 index 000000000000..624678559d79 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java @@ -0,0 +1,23 @@ +package org.eclipse.jetty.io; + +public class AdapterMemoryPool implements MemoryPool +{ + private final ByteBufferPool byteBufferPool; + + public AdapterMemoryPool(ByteBufferPool byteBufferPool) + { + this.byteBufferPool = byteBufferPool; + } + + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + return new RetainableByteBuffer(byteBufferPool, size, direct); + } + + @Override + public void release(RetainableByteBuffer buffer) + { + buffer.release(); + } +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java index 2c67e29fdc6a..6e3cb418cca2 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java @@ -30,7 +30,7 @@ * if they are released, they may be recycled and reused, otherwise they will be garbage * collected as usual.

*/ -public interface ByteBufferPool +public interface ByteBufferPool extends MemoryPool { /** *

Requests a {@link ByteBuffer} of the given size.

diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java new file mode 100644 index 000000000000..b4eee4194c90 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java @@ -0,0 +1,21 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +public interface MemoryPool +{ + T acquire(int size, boolean direct); + + void release(T buffer); +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 7e2f0a02dff7..5c6bd414f715 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -22,7 +22,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject; @ManagedObject -public class RetainableByteBufferPool +public class RetainableByteBufferPool implements MemoryPool { private final Pool[] _direct; private final Pool[] _indirect; @@ -57,6 +57,7 @@ public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, in _indirect = indirectArray; } + @Override public RetainableByteBuffer acquire(int size, boolean direct) { int capacity = (bucketFor(size) + 1) * _factor; @@ -84,6 +85,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) return buffer; } + @Override public void release(RetainableByteBuffer buffer) { if (buffer == null) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 472d44c596d1..9eef1d4a339f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -32,9 +32,11 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractEndPoint; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; @@ -109,7 +111,7 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; - private final RetainableByteBufferPool _retainableBufferPool; + private final MemoryPool _retainableBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; @@ -194,13 +196,15 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint this(null, byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } - public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + public SslConnection(MemoryPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. // onFillable() does not block and will only wakeup another thread to do the actual reading and handling. super(endPoint, executor); this._bufferPool = byteBufferPool; + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); this._retainableBufferPool = retainableByteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = newDecryptedEndPoint(); @@ -336,12 +340,7 @@ private int getBufferSize(ToIntFunction bufferSizeFn) private void acquireEncryptedInput() { if (_encryptedInput == null) - { - if (_retainableBufferPool == null) - _encryptedInput = new RetainableByteBuffer(_bufferPool, getPacketBufferSize(), _encryptedDirectBuffers); - else - _encryptedInput = _retainableBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); - } + _encryptedInput = _retainableBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); } private void acquireEncryptedOutput() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index eb3ffeccce31..bc8f67235a17 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -146,7 +146,6 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co private final Executor _executor; private final Scheduler _scheduler; private final ByteBufferPool _byteBufferPool; - private final RetainableByteBufferPool _retainableByteBufferPool; private final Thread[] _acceptors; private final Set _endpoints = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set _immutableEndPoints = Collections.unmodifiableSet(_endpoints); @@ -191,8 +190,8 @@ public AbstractConnector( _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); - _retainableByteBufferPool = retainableByteBufferPool != null ? retainableByteBufferPool : new RetainableByteBufferPool(); - addBean(_retainableByteBufferPool); + retainableByteBufferPool = retainableByteBufferPool != null ? retainableByteBufferPool : new RetainableByteBufferPool(); + addBean(retainableByteBufferPool); addEventListener(new Container.Listener() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 661b96f570d2..5c0182ad4584 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -30,10 +30,12 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; @@ -57,7 +59,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpConfiguration _config; private final Connector _connector; private final ByteBufferPool _bufferPool; - private final RetainableByteBufferPool _retainableBufferPool; + private final MemoryPool _retainableByteBufferPool; private final HttpInput _input; private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; @@ -97,7 +99,10 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _retainableBufferPool = connector.getBean(RetainableByteBufferPool.class); + MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(_bufferPool); + _retainableByteBufferPool = retainableByteBufferPool; _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); @@ -239,12 +244,7 @@ void releaseRequestBuffer() public ByteBuffer getRequestBuffer() { if (_retainableByteBuffer == null) - { - if (_retainableBufferPool == null) - _retainableByteBuffer = new RetainableByteBuffer(_bufferPool, getInputBufferSize(), isUseInputDirectByteBuffers()); - else - _retainableByteBuffer = _retainableBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); - } + _retainableByteBuffer = _retainableByteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); return _retainableByteBuffer.getBuffer(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 6b523ccf58fe..53de2b62fe41 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -19,8 +19,12 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AdapterMemoryPool; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; @@ -161,7 +165,11 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getBean(RetainableByteBufferPool.class), connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index d30ac0bad0f2..8d0bc921edec 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -37,8 +37,11 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; @@ -448,7 +451,9 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = wsClient.getWebSocketComponents().getBean(RetainableByteBufferPool.class); + MemoryPool retainableByteBufferPool = wsClient.getWebSocketComponents().getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 2fa708160409..01199ebf2c32 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -26,8 +26,8 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.component.Dumpable; @@ -53,7 +53,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private final AutoLock lock = new AutoLock(); private final ByteBufferPool bufferPool; - private final RetainableByteBufferPool retainableByteBufferPool; + private final MemoryPool retainableByteBufferPool; private final Generator generator; private final Parser parser; private final WebSocketCoreSession coreSession; @@ -79,7 +79,7 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, - RetainableByteBufferPool retainableByteBufferPool, + MemoryPool retainableByteBufferPool, WebSocketCoreSession coreSession) { this(endp, executor, scheduler, bufferPool, retainableByteBufferPool, coreSession, null); @@ -103,7 +103,7 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, - RetainableByteBufferPool retainableByteBufferPool, + MemoryPool retainableByteBufferPool, WebSocketCoreSession coreSession, Random randomMask) { @@ -113,6 +113,7 @@ public WebSocketConnection(EndPoint endp, Objects.requireNonNull(coreSession, "Session"); Objects.requireNonNull(executor, "Executor"); Objects.requireNonNull(bufferPool, "ByteBufferPool"); + Objects.requireNonNull(retainableByteBufferPool, "MemoryPool"); this.bufferPool = bufferPool; this.retainableByteBufferPool = retainableByteBufferPool; diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index 734091f247e9..5dc951564c5f 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -24,7 +24,8 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpTransport; @@ -217,7 +218,7 @@ protected void handle(Runnable runnable) protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession); - protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) + protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, MemoryPool retainableByteBufferPool, WebSocketCoreSession coreSession) { return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index ee09b8660e5b..04afa058c276 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -23,6 +23,10 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; +import org.eclipse.jetty.io.AdapterMemoryPool; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; @@ -95,7 +99,11 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web { HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); - return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), connector.getBean(RetainableByteBufferPool.class), coreSession); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } @Override diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index af080fead85d..ee5a88975444 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -19,7 +19,11 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.AdapterMemoryPool; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; @@ -79,7 +83,11 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); - return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), connector.getBean(RetainableByteBufferPool.class), coreSession); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } @Override From db9ba6c419958a19dd916d7032ff5e4c28a73e87 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 11:13:54 +0200 Subject: [PATCH 20/75] make RetainableByteBufferPool never null Signed-off-by: Ludovic Orban --- .../jetty/websocket/core/internal/WebSocketConnection.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 01199ebf2c32..eaceb90a380e 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -290,10 +290,7 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer(int capacity) { - if (retainableByteBufferPool == null) - return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers()); - else - return retainableByteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); + return retainableByteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); } private void releaseNetworkBuffer() From f8314ef96a5bc52a5de66d5add22fda38a1ac8d2 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 11:22:14 +0200 Subject: [PATCH 21/75] make H2 connection use RetainableByteBuffer pool Signed-off-by: Ludovic Orban --- .../eclipse/jetty/http2/HTTP2Connection.java | 44 +++++++++++++++---- .../jetty/io/RetainableByteBuffer.java | 4 +- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index b2b62a4499ee..092ac53b1e14 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -23,12 +23,12 @@ import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.parser.Parser; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -51,7 +51,6 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final Queue tasks = new ArrayDeque<>(); private final HTTP2Producer producer = new HTTP2Producer(); private final AtomicLong bytesIn = new AtomicLong(); - private final ByteBufferPool byteBufferPool; private final MemoryPool retainableByteBufferPool; private final Parser parser; private final ISession session; @@ -62,13 +61,12 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { - this(byteBufferPool, null, executor, endPoint, parser, session, bufferSize); + this(new AdapterMemoryPool(byteBufferPool), executor, endPoint, parser, session, bufferSize); } - public HTTP2Connection(ByteBufferPool byteBufferPool, MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + public HTTP2Connection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); - this.byteBufferPool = byteBufferPool; this.retainableByteBufferPool = retainableByteBufferPool; this.parser = parser; this.session = session; @@ -430,16 +428,44 @@ public void onConnectionFailure(int error, String reason) } } - private class NetworkBuffer extends RetainableByteBuffer implements Callback + private class NetworkBuffer implements Callback { + + private final RetainableByteBuffer delegate; + private NetworkBuffer() { - super(retainableByteBufferPool == null ? byteBufferPool : null, bufferSize, isUseInputDirectByteBuffers()); + delegate = retainableByteBufferPool.acquire(bufferSize, isUseInputDirectByteBuffers()); + } + + public ByteBuffer getBuffer() + { + return delegate.getBuffer(); + } + + public int getReferences() + { + return delegate.getReferences(); + } + + public boolean hasRemaining() + { + return delegate.hasRemaining(); + } + + public int release() + { + return delegate.release(); + } + + public void retain() + { + delegate.retain(); } private void put(ByteBuffer source) { - BufferUtil.append(getBuffer(), source); + BufferUtil.append(delegate.getBuffer(), source); } @Override @@ -456,7 +482,7 @@ public void failed(Throwable failure) private void completed(Throwable failure) { - if (release() == 0) + if (delegate.release() == 0) { if (LOG.isDebugEnabled()) LOG.debug("Released retained {}", this, failure); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 29048f65abb6..03248d6ce525 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -34,12 +34,12 @@ public class RetainableByteBuffer implements Retainable, Attachable private final AtomicInteger references; private Pool.Entry entry; - RetainableByteBuffer(int size, boolean direct) + protected RetainableByteBuffer(int size, boolean direct) { this(null, size, direct); } - public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) + RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) { this.pool = pool; if (pool != null) From 27a94dbbd18a627818924e1f39781f005333cfe4 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 11:43:17 +0200 Subject: [PATCH 22/75] make H2 connection use RetainableByteBuffer pool Signed-off-by: Ludovic Orban --- .../http2/client/HTTP2ClientConnectionFactory.java | 14 +++++++++++--- .../org/eclipse/jetty/http2/HTTP2Connection.java | 9 +-------- .../AbstractHTTP2ServerConnectionFactory.java | 10 +++++++++- .../jetty/http2/server/HTTP2ServerConnection.java | 7 ++++--- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index 228267b308f6..ffcdd0240cb8 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -27,10 +27,14 @@ import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.generator.Generator; import org.eclipse.jetty.http2.parser.Parser; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -67,7 +71,11 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, executor, endPoint, + MemoryPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + + HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); connection.setUseInputDirectByteBuffers(client.isUseInputDirectByteBuffers()); connection.setUseOutputDirectByteBuffers(client.isUseOutputDirectByteBuffers()); @@ -81,9 +89,9 @@ private static class HTTP2ClientConnection extends HTTP2Connection implements Ca private final Promise promise; private final Session.Listener listener; - private HTTP2ClientConnection(HTTP2Client client, ByteBufferPool byteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) + private HTTP2ClientConnection(HTTP2Client client, MemoryPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) { - super(byteBufferPool, executor, endpoint, parser, session, bufferSize); + super(retainableByteBufferPool, executor, endpoint, parser, session, bufferSize); this.client = client; this.promise = promise; this.listener = listener; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 092ac53b1e14..eee8161e998f 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -23,8 +23,6 @@ import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.parser.Parser; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AdapterMemoryPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; @@ -59,12 +57,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private boolean useInputDirectByteBuffers; private boolean useOutputDirectByteBuffers; - public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) - { - this(new AdapterMemoryPool(byteBufferPool), executor, endPoint, parser, session, bufferSize); - } - - public HTTP2Connection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + protected HTTP2Connection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); this.retainableByteBufferPool = retainableByteBufferPool; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 76b91c92776b..a1bcbe60187b 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -35,8 +35,12 @@ import org.eclipse.jetty.http2.parser.RateControl; import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.WindowRateControl; +import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; @@ -279,7 +283,11 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), + MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(connector.getByteBufferPool()); + + HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers()); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index f3037941374c..67063fba9e98 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -45,8 +45,9 @@ import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.SettingsBodyParser; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.util.BufferUtil; @@ -87,9 +88,9 @@ public static boolean isSupportedProtocol(String protocol) private final HttpConfiguration httpConfig; private boolean recycleHttpChannels = true; - public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) + public HTTP2ServerConnection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) { - super(byteBufferPool, executor, endPoint, parser, session, inputBufferSize); + super(retainableByteBufferPool, executor, endPoint, parser, session, inputBufferSize); this.listener = listener; this.httpConfig = httpConfig; } From c4377848de2d3b3edc522216bbab23d8b911688b Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 13:21:10 +0200 Subject: [PATCH 23/75] add missing header Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/io/AdapterMemoryPool.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java index 624678559d79..d80f409240f6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java @@ -1,3 +1,16 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + package org.eclipse.jetty.io; public class AdapterMemoryPool implements MemoryPool From 6eef227fe22ca7889d5914b1e72103d1f4db58ca Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 17:57:55 +0200 Subject: [PATCH 24/75] generalize RetainableByteBuffer Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/AdapterMemoryPool.java | 8 ++- .../jetty/io/RetainableByteBuffer.java | 61 +++---------------- .../jetty/io/RetainableByteBufferPool.java | 23 ++++++- 3 files changed, 35 insertions(+), 57 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java index d80f409240f6..e422c0d3f0a0 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java @@ -13,19 +13,25 @@ package org.eclipse.jetty.io; +import java.nio.ByteBuffer; +import java.util.function.Consumer; + public class AdapterMemoryPool implements MemoryPool { private final ByteBufferPool byteBufferPool; + private final Consumer releaser; public AdapterMemoryPool(ByteBufferPool byteBufferPool) { this.byteBufferPool = byteBufferPool; + this.releaser = byteBufferPool::release; } @Override public RetainableByteBuffer acquire(int size, boolean direct) { - return new RetainableByteBuffer(byteBufferPool, size, direct); + ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); + return new RetainableByteBuffer(byteBuffer, releaser); } @Override diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 03248d6ce525..4d432714bc25 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -15,10 +15,9 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; -import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.Retainable; /** @@ -27,30 +26,16 @@ * initially 1, incremented with {@link #retain()} and decremented with {@link #release()}. The buffer * is released to the pool when the reference count is decremented to 0.

*/ -public class RetainableByteBuffer implements Retainable, Attachable +public class RetainableByteBuffer implements Retainable { - private final ByteBufferPool pool; private final ByteBuffer buffer; private final AtomicInteger references; - private Pool.Entry entry; + private final Consumer releaser; - protected RetainableByteBuffer(int size, boolean direct) + RetainableByteBuffer(ByteBuffer buffer, Consumer releaser) { - this(null, size, direct); - } - - RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) - { - this.pool = pool; - if (pool != null) - { - this.buffer = pool.acquire(size, direct); - } - else - { - this.buffer = direct ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); - BufferUtil.clear(this.buffer); - } + this.releaser = releaser; + this.buffer = buffer; this.references = new AtomicInteger(1); } @@ -59,20 +44,6 @@ public int capacity() return buffer.capacity(); } - @Override - public Object getAttachment() - { - return entry; - } - - @Override - public void setAttachment(Object attachment) - { - @SuppressWarnings("unchecked") - Pool.Entry entry = (Pool.Entry)attachment; - this.entry = entry; - } - public ByteBuffer getBuffer() { return buffer; @@ -91,30 +62,14 @@ public boolean isDirect() @Override public void retain() { - while (true) - { - int r = references.get(); - if (r == 0 && pool != null) - throw new IllegalStateException("released " + this); - if (references.compareAndSet(r, r + 1)) - break; - } + references.incrementAndGet(); } public int release() { int ref = references.decrementAndGet(); if (ref == 0) - if (pool != null) - { - pool.release(buffer); - } - else - { - BufferUtil.clear(buffer); - if (entry != null) - entry.release(); - } + releaser.accept(buffer); else if (ref < 0) throw new IllegalStateException("already released " + this); return ref; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 5c6bd414f715..0a7b204768b2 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -13,10 +13,13 @@ package org.eclipse.jetty.io; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Supplier; +import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; @@ -63,19 +66,26 @@ public RetainableByteBuffer acquire(int size, boolean direct) int capacity = (bucketFor(size) + 1) * _factor; Pool bucket = bucketFor(size, direct, this::newBucket); if (bucket == null) - return new RetainableByteBuffer(capacity, direct); + return newRetainableByteBuffer(capacity, direct, byteBuffer -> {}); Pool.Entry entry = bucket.acquire(); RetainableByteBuffer buffer; if (entry == null) { - buffer = new RetainableByteBuffer(capacity, direct); Pool.Entry reservedEntry = bucket.reserve(); if (reservedEntry != null) { - buffer.setAttachment(reservedEntry); + buffer = newRetainableByteBuffer(capacity, direct, byteBuffer -> + { + BufferUtil.clear(byteBuffer); + reservedEntry.release(); + }); reservedEntry.enable(buffer, true); } + else + { + buffer = newRetainableByteBuffer(capacity, direct, byteBuffer -> {}); + } } else { @@ -85,6 +95,13 @@ public RetainableByteBuffer acquire(int size, boolean direct) return buffer; } + private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direct, Consumer releaser) + { + ByteBuffer buffer = direct ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); + BufferUtil.clear(buffer); + return new RetainableByteBuffer(buffer, releaser); + } + @Override public void release(RetainableByteBuffer buffer) { From 2f6000aa93f8d3247326c64fdc85f8a456320e16 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 9 Jun 2021 17:58:35 +0200 Subject: [PATCH 25/75] enable thread-local cache on the pool Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/io/RetainableByteBufferPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 0a7b204768b2..c5a852645478 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -136,7 +136,7 @@ private Pool[] bucketsFor(boolean direct) private Pool newBucket() { - return new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize); + return new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize, true); } @ManagedAttribute("The number of pooled direct ByteBuffers") From 1879cc958685baeba67d993b7c44a77edcc2e70c Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 10 Jun 2021 09:15:56 +0200 Subject: [PATCH 26/75] do not handle null retainableByteBufferPool Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/io/ssl/SslConnection.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 9eef1d4a339f..e0ec2579f29e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -38,7 +38,6 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -111,7 +110,7 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; - private final MemoryPool _retainableBufferPool; + private final MemoryPool _retainableByteBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; @@ -193,7 +192,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { - this(null, byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + this(new AdapterMemoryPool(byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } public SslConnection(MemoryPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, @@ -203,9 +202,7 @@ public SslConnection(MemoryPool retainableByteBufferPool, // onFillable() does not block and will only wakeup another thread to do the actual reading and handling. super(endPoint, executor); this._bufferPool = byteBufferPool; - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); - this._retainableBufferPool = retainableByteBufferPool; + this._retainableByteBufferPool = retainableByteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = newDecryptedEndPoint(); this._encryptedDirectBuffers = useDirectBuffersForEncryption; @@ -340,7 +337,7 @@ private int getBufferSize(ToIntFunction bufferSizeFn) private void acquireEncryptedInput() { if (_encryptedInput == null) - _encryptedInput = _retainableBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); + _encryptedInput = _retainableByteBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); } private void acquireEncryptedOutput() From 5fc4f5140b2c5285f9bf5e41d8e1823fcede3737 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 10 Jun 2021 16:30:11 +0200 Subject: [PATCH 27/75] make http client register RetainableByteBufferPool Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/client/HttpClient.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 7e672fd42b55..87fe1befc296 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -58,6 +58,7 @@ import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Jetty; @@ -202,6 +203,8 @@ protected void doStart() throws Exception Scheduler scheduler = getScheduler(); if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); + if (getBean(RetainableByteBufferPool.class) == null) + addBean(new RetainableByteBufferPool(0, 2048, 65536, ProcessorUtils.availableProcessors() * 2)); if (resolver == null) setSocketAddressResolver(new SocketAddressResolver.Async(getExecutor(), getScheduler(), getAddressResolutionTimeout())); From 06580bf8f56b27fb2f7dadfe3fbda94f2afaa100 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 11 Jun 2021 08:58:03 +0200 Subject: [PATCH 28/75] fix concurrency Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBufferPool.java | 63 +++++++++---------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index c5a852645478..df520cba09e3 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -14,10 +14,8 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Objects; +import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.function.Consumer; -import java.util.function.Supplier; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; @@ -27,8 +25,8 @@ @ManagedObject public class RetainableByteBufferPool implements MemoryPool { - private final Pool[] _direct; - private final Pool[] _indirect; + private final AtomicReferenceArray> _direct; + private final AtomicReferenceArray> _indirect; private final int _factor; private final int _maxBucketSize; private final int _minCapacity; @@ -52,19 +50,15 @@ public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, in int length = maxCapacity / _factor; - @SuppressWarnings("unchecked") - Pool[] directArray = new Pool[length]; - _direct = directArray; - @SuppressWarnings("unchecked") - Pool[] indirectArray = new Pool[length]; - _indirect = indirectArray; + _direct = new AtomicReferenceArray<>(length); + _indirect = new AtomicReferenceArray<>(length); } @Override public RetainableByteBuffer acquire(int size, boolean direct) { - int capacity = (bucketFor(size) + 1) * _factor; - Pool bucket = bucketFor(size, direct, this::newBucket); + int capacity = (bucketIndexFor(size) + 1) * _factor; + Pool bucket = bucketFor(size, direct); if (bucket == null) return newRetainableByteBuffer(capacity, direct, byteBuffer -> {}); Pool.Entry entry = bucket.acquire(); @@ -110,35 +104,29 @@ public void release(RetainableByteBuffer buffer) buffer.release(); } - private Pool bucketFor(int capacity, boolean direct, Supplier> newBucket) + private Pool bucketFor(int capacity, boolean direct) { if (capacity < _minCapacity) return null; - int b = bucketFor(capacity); - if (b >= _direct.length) + int idx = bucketIndexFor(capacity); + AtomicReferenceArray> buckets = direct ? _direct : _indirect; + if (idx >= buckets.length()) return null; - Pool[] buckets = bucketsFor(direct); - Pool bucket = buckets[b]; - if (bucket == null && newBucket != null) - buckets[b] = bucket = newBucket.get(); + Pool bucket = buckets.get(idx); + if (bucket == null) + { + bucket = new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize, true); + if (!buckets.compareAndSet(idx, null, bucket)) + bucket = buckets.get(idx); + } return bucket; } - private int bucketFor(int capacity) + private int bucketIndexFor(int capacity) { return (capacity - 1) / _factor; } - private Pool[] bucketsFor(boolean direct) - { - return direct ? _direct : _indirect; - } - - private Pool newBucket() - { - return new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize, true); - } - @ManagedAttribute("The number of pooled direct ByteBuffers") public long getDirectByteBufferCount() { @@ -153,9 +141,14 @@ public long getHeapByteBufferCount() private long getByteBufferCount(boolean direct) { - return Arrays.stream(bucketsFor(direct)) - .filter(Objects::nonNull) - .mapToLong(Pool::size) - .sum(); + long total = 0L; + AtomicReferenceArray> buckets = direct ? _direct : _indirect; + for (int i = 0; i < buckets.length(); i++) + { + Pool bucket = buckets.getOpaque(i); + if (bucket != null) + total += bucket.size(); + } + return total; } } From 6085a77080856e496fc9b10566feafdb5f0be902 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Tue, 15 Jun 2021 10:40:50 +0200 Subject: [PATCH 29/75] add & fix javadoc Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/io/AdapterMemoryPool.java | 4 ++++ .../main/java/org/eclipse/jetty/io/MemoryPool.java | 14 ++++++++++++++ .../org/eclipse/jetty/io/RetainableByteBuffer.java | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java index e422c0d3f0a0..9e5913dfeb18 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java @@ -16,6 +16,10 @@ import java.nio.ByteBuffer; import java.util.function.Consumer; +/** + * An adapter class which exposes a {@link ByteBufferPool} as a + * {@link MemoryPool} of {@link RetainableByteBuffer}. + */ public class AdapterMemoryPool implements MemoryPool { private final ByteBufferPool byteBufferPool; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java index b4eee4194c90..3c486f78d554 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java @@ -13,9 +13,23 @@ package org.eclipse.jetty.io; +/** + * A pool of memory buffers. + * @param The memory buffer type. + */ public interface MemoryPool { + /** + * Acquire a memory buffer from the pool. + * @param size The size of the buffer. The returned buffer will have at least this capacity. + * @param direct true if a direct memory buffer is needed, false otherwise. + * @return a memory buffer. + */ T acquire(int size, boolean direct); + /** + * Release a previously acquired memory buffer to the pool. + * @param buffer the memory buffer to release. + */ void release(T buffer); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 4d432714bc25..879040b1cafe 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -22,7 +22,7 @@ /** * A Retainable ByteBuffer. - *

Acquires a ByteBuffer from a {@link ByteBufferPool} and maintains a reference count that is + *

A ByteBuffer which maintains a reference count that is * initially 1, incremented with {@link #retain()} and decremented with {@link #release()}. The buffer * is released to the pool when the reference count is decremented to 0.

*/ From c00a37a1fe572ab38cb3d3aaa202feb45d1cda92 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 17 Jun 2021 11:33:50 +0200 Subject: [PATCH 30/75] improve javadoc Signed-off-by: Ludovic Orban --- jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java index 3c486f78d554..dd551383599b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java @@ -14,7 +14,8 @@ package org.eclipse.jetty.io; /** - * A pool of memory buffers. + * A Pool of objects representing memory that can be acquired based on size and direction. The held instances may be the memory + * component itself (e.g.: {@link java.nio.ByteBuffer}) or an abstraction providing access to the memory component. * @param The memory buffer type. */ public interface MemoryPool From 3a98a3fc97c6bf35e5466ee01d3ef8f924c51afd Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 17 Jun 2021 11:42:26 +0200 Subject: [PATCH 31/75] pre-allocate buckets Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBufferPool.java | 45 ++++++++----------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index df520cba09e3..5bb02c3ec9a1 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -14,7 +14,7 @@ package org.eclipse.jetty.io; import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.Arrays; import java.util.function.Consumer; import org.eclipse.jetty.util.BufferUtil; @@ -25,10 +25,9 @@ @ManagedObject public class RetainableByteBufferPool implements MemoryPool { - private final AtomicReferenceArray> _direct; - private final AtomicReferenceArray> _indirect; + private final Pool[] _direct; + private final Pool[] _indirect; private final int _factor; - private final int _maxBucketSize; private final int _minCapacity; public RetainableByteBufferPool() @@ -39,7 +38,6 @@ public RetainableByteBufferPool() public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) { _factor = factor <= 0 ? 1024 : factor; - _maxBucketSize = maxBucketSize; if (minCapacity <= 0) minCapacity = 0; _minCapacity = minCapacity; @@ -50,8 +48,17 @@ public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, in int length = maxCapacity / _factor; - _direct = new AtomicReferenceArray<>(length); - _indirect = new AtomicReferenceArray<>(length); + @SuppressWarnings("unchecked") + Pool[] directArray = new Pool[length]; + @SuppressWarnings("unchecked") + Pool[] indirectArray = new Pool[length]; + for (int i = 0; i < directArray.length; i++) + { + directArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); + indirectArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); + } + _direct = directArray; + _indirect = indirectArray; } @Override @@ -109,17 +116,10 @@ private Pool bucketFor(int capacity, boolean direct) if (capacity < _minCapacity) return null; int idx = bucketIndexFor(capacity); - AtomicReferenceArray> buckets = direct ? _direct : _indirect; - if (idx >= buckets.length()) + Pool[] buckets = direct ? _direct : _indirect; + if (idx >= buckets.length) return null; - Pool bucket = buckets.get(idx); - if (bucket == null) - { - bucket = new Pool<>(Pool.StrategyType.THREAD_ID, _maxBucketSize, true); - if (!buckets.compareAndSet(idx, null, bucket)) - bucket = buckets.get(idx); - } - return bucket; + return buckets[idx]; } private int bucketIndexFor(int capacity) @@ -141,14 +141,7 @@ public long getHeapByteBufferCount() private long getByteBufferCount(boolean direct) { - long total = 0L; - AtomicReferenceArray> buckets = direct ? _direct : _indirect; - for (int i = 0; i < buckets.length(); i++) - { - Pool bucket = buckets.getOpaque(i); - if (bucket != null) - total += bucket.size(); - } - return total; + Pool[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(Pool::size).sum(); } } From 3412ee7ce94423a421effbbf6717897553defd93 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 17 Jun 2021 13:21:42 +0200 Subject: [PATCH 32/75] add helper method that either finds or wraps a memory pool Signed-off-by: Ludovic Orban --- .../jetty/client/http/HttpReceiverOverHTTP.java | 8 +------- .../client/http/HttpConnectionOverFCGI.java | 8 +------- .../client/HTTP2ClientConnectionFactory.java | 6 +----- .../AbstractHTTP2ServerConnectionFactory.java | 6 +----- .../java/org/eclipse/jetty/io/MemoryPool.java | 17 +++++++++++++++++ .../eclipse/jetty/server/HttpConnection.java | 7 +------ .../jetty/server/SslConnectionFactory.java | 6 +----- .../core/client/CoreClientUpgradeRequest.java | 6 +----- .../core/server/internal/RFC6455Handshaker.java | 6 +----- .../core/server/internal/RFC8441Handshaker.java | 6 +----- 10 files changed, 26 insertions(+), 50 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 7e05d2b4fbca..1c1a8c23256e 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -29,12 +29,9 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.AdapterMemoryPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; @@ -66,10 +63,7 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } - MemoryPool retainableByteBufferPool = httpClient.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(httpClient.getByteBufferPool()); - this.retainableByteBufferPool = retainableByteBufferPool; + this.retainableByteBufferPool = MemoryPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); } @Override diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index efa800b6cd2d..b350119e1671 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -44,12 +44,9 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AdapterMemoryPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -87,10 +84,7 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr requests.addLast(0); HttpClient client = destination.getHttpClient(); - MemoryPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(client.getByteBufferPool()); - this.retainableByteBufferPool = retainableByteBufferPool; + this.retainableByteBufferPool = MemoryPool.findOrAdapt(client, client.getByteBufferPool()); } public HttpDestination getHttpDestination() diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index ffcdd0240cb8..174d0ba4246d 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -27,14 +27,12 @@ import org.eclipse.jetty.http2.frames.WindowUpdateFrame; import org.eclipse.jetty.http2.generator.Generator; import org.eclipse.jetty.http2.parser.Parser; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -71,9 +69,7 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = client.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(client, byteBufferPool); HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index a1bcbe60187b..adf989a6059f 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -35,12 +35,10 @@ import org.eclipse.jetty.http2.parser.RateControl; import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.WindowRateControl; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; @@ -283,9 +281,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(connector.getByteBufferPool()); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, connector.getByteBufferPool()); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java index dd551383599b..12c3fc6f42bc 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java @@ -13,6 +13,8 @@ package org.eclipse.jetty.io; +import org.eclipse.jetty.util.component.Container; + /** * A Pool of objects representing memory that can be acquired based on size and direction. The held instances may be the memory * component itself (e.g.: {@link java.nio.ByteBuffer}) or an abstraction providing access to the memory component. @@ -33,4 +35,19 @@ public interface MemoryPool * @param buffer the memory buffer to release. */ void release(T buffer); + + /** + * Find a {@link MemoryPool} of {@link RetainableByteBuffer} implementation in the given container, or wrap the given + * {@link ByteBufferPool} with an adapter. + * @param container the container to search for an existing memory pool. + * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. + * @return the memory pool found or the wrapped one. + */ + static MemoryPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) + { + MemoryPool retainableByteBufferPool = container.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + return retainableByteBufferPool; + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 5c0182ad4584..77631da9c2ad 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -30,14 +30,12 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -99,10 +97,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(_bufferPool); - _retainableByteBufferPool = retainableByteBufferPool; + _retainableByteBufferPool = MemoryPool.findOrAdapt(connector, _bufferPool);; _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 53de2b62fe41..1542cef725bf 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -19,13 +19,11 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.util.annotation.Name; @@ -166,9 +164,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 8d0bc921edec..9c68f66acac2 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -37,12 +37,10 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -451,9 +449,7 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - MemoryPool retainableByteBufferPool = wsClient.getWebSocketComponents().getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(bufferPool); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index 04afa058c276..fa99692f7eeb 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -23,11 +23,9 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -100,9 +98,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index ee5a88975444..f724dcf39209 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -19,12 +19,10 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -84,9 +82,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = connector.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } From 066343c7c2824c215e97501928de4981ea448929 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 18 Jun 2021 11:47:48 +0200 Subject: [PATCH 33/75] handle review comments Signed-off-by: Ludovic Orban --- .../client/http/HttpReceiverOverHTTP.java | 3 +- .../client/http/HttpConnectionOverFCGI.java | 3 +- .../client/HTTP2ClientConnectionFactory.java | 3 +- .../AbstractHTTP2ServerConnectionFactory.java | 3 +- .../eclipse/jetty/io/AdapterMemoryPool.java | 46 ------------------- .../java/org/eclipse/jetty/io/MemoryPool.java | 15 ------ .../jetty/io/RetainableByteBufferPool.java | 45 ++++++++++++++++++ .../eclipse/jetty/io/ssl/SslConnection.java | 3 +- .../jetty/server/AbstractConnector.java | 3 +- .../eclipse/jetty/server/HttpConnection.java | 3 +- .../jetty/server/SslConnectionFactory.java | 3 +- .../core/client/CoreClientUpgradeRequest.java | 3 +- .../server/internal/RFC6455Handshaker.java | 3 +- .../server/internal/RFC8441Handshaker.java | 3 +- 14 files changed, 66 insertions(+), 73 deletions(-) delete mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 1c1a8c23256e..1f43fb22e1d3 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; @@ -63,7 +64,7 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } - this.retainableByteBufferPool = MemoryPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); } @Override diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index b350119e1671..9bb6b2e953a4 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -47,6 +47,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -84,7 +85,7 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr requests.addLast(0); HttpClient client = destination.getHttpClient(); - this.retainableByteBufferPool = MemoryPool.findOrAdapt(client, client.getByteBufferPool()); + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); } public HttpDestination getHttpDestination() diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index 174d0ba4246d..a2da424d754a 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -69,7 +70,7 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(client, byteBufferPool); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, byteBufferPool); HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index adf989a6059f..0181274655ef 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; @@ -281,7 +282,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, connector.getByteBufferPool()); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java deleted file mode 100644 index 9e5913dfeb18..000000000000 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/AdapterMemoryPool.java +++ /dev/null @@ -1,46 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// ======================================================================== -// - -package org.eclipse.jetty.io; - -import java.nio.ByteBuffer; -import java.util.function.Consumer; - -/** - * An adapter class which exposes a {@link ByteBufferPool} as a - * {@link MemoryPool} of {@link RetainableByteBuffer}. - */ -public class AdapterMemoryPool implements MemoryPool -{ - private final ByteBufferPool byteBufferPool; - private final Consumer releaser; - - public AdapterMemoryPool(ByteBufferPool byteBufferPool) - { - this.byteBufferPool = byteBufferPool; - this.releaser = byteBufferPool::release; - } - - @Override - public RetainableByteBuffer acquire(int size, boolean direct) - { - ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); - return new RetainableByteBuffer(byteBuffer, releaser); - } - - @Override - public void release(RetainableByteBuffer buffer) - { - buffer.release(); - } -} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java index 12c3fc6f42bc..5a8704c00737 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java @@ -35,19 +35,4 @@ public interface MemoryPool * @param buffer the memory buffer to release. */ void release(T buffer); - - /** - * Find a {@link MemoryPool} of {@link RetainableByteBuffer} implementation in the given container, or wrap the given - * {@link ByteBufferPool} with an adapter. - * @param container the container to search for an existing memory pool. - * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. - * @return the memory pool found or the wrapped one. - */ - static MemoryPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) - { - MemoryPool retainableByteBufferPool = container.getBean(RetainableByteBufferPool.class); - if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); - return retainableByteBufferPool; - } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 5bb02c3ec9a1..c288b517d40b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.component.Container; @ManagedObject public class RetainableByteBufferPool implements MemoryPool @@ -144,4 +145,48 @@ private long getByteBufferCount(boolean direct) Pool[] buckets = direct ? _direct : _indirect; return Arrays.stream(buckets).mapToLong(Pool::size).sum(); } + + /** + * Find a {@link MemoryPool} of {@link RetainableByteBuffer} implementation in the given container, or wrap the given + * {@link ByteBufferPool} with an adapter. + * @param container the container to search for an existing memory pool. + * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. + * @return the memory pool found or the wrapped one. + */ + public static MemoryPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) + { + MemoryPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + return retainableByteBufferPool; + } + + /** + * An adapter class which exposes a {@link ByteBufferPool} as a + * {@link MemoryPool} of {@link RetainableByteBuffer}. + */ + private static class AdapterMemoryPool implements MemoryPool + { + private final ByteBufferPool byteBufferPool; + private final Consumer releaser; + + public AdapterMemoryPool(ByteBufferPool byteBufferPool) + { + this.byteBufferPool = byteBufferPool; + this.releaser = byteBufferPool::release; + } + + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); + return new RetainableByteBuffer(byteBuffer, releaser); + } + + @Override + public void release(RetainableByteBuffer buffer) + { + buffer.release(); + } + } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index e0ec2579f29e..0296769bcab1 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -38,6 +38,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -192,7 +193,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { - this(new AdapterMemoryPool(byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + this(RetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } public SslConnection(MemoryPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index bc8f67235a17..9a5d3b56a0f6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -190,8 +190,7 @@ public AbstractConnector( _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); - retainableByteBufferPool = retainableByteBufferPool != null ? retainableByteBufferPool : new RetainableByteBufferPool(); - addBean(retainableByteBufferPool); + addBean(retainableByteBufferPool == null ? new RetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); addEventListener(new Container.Listener() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 77631da9c2ad..fc115f0e97ba 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -36,6 +36,7 @@ import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -97,7 +98,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _retainableByteBufferPool = MemoryPool.findOrAdapt(connector, _bufferPool);; + _retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, _bufferPool);; _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 1542cef725bf..b5c8f2da1b87 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.util.annotation.Name; @@ -164,7 +165,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 9c68f66acac2..8cbd29e87fa8 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -41,6 +41,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -449,7 +450,7 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index fa99692f7eeb..825937f4c39f 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -26,6 +26,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -98,7 +99,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index f724dcf39209..c2f833a0eb83 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -23,6 +23,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -82,7 +83,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = MemoryPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } From 34bbce0be68599847236198d8e754cb22221ab9d Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 18 Jun 2021 12:37:07 +0200 Subject: [PATCH 34/75] handle review comments Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 0296769bcab1..21bee928eba2 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.AbstractEndPoint; -import org.eclipse.jetty.io.AdapterMemoryPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; From 5f0b54952c4559fca57cb65a149155da732d50ae Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 09:12:40 +0200 Subject: [PATCH 35/75] rename Signed-off-by: Ludovic Orban --- .../main/java/org/eclipse/jetty/client/HttpClient.java | 6 +++--- .../eclipse/jetty/client/http/HttpReceiverOverHTTP.java | 4 ++-- .../jetty/fcgi/client/http/HttpConnectionOverFCGI.java | 4 ++-- .../jetty/http2/client/HTTP2ClientConnectionFactory.java | 4 ++-- .../server/AbstractHTTP2ServerConnectionFactory.java | 4 ++-- .../main/java/org/eclipse/jetty/io/ByteBufferPool.java | 2 +- ...fferPool.java => DefaultRetainableByteBufferPool.java} | 8 ++++---- .../main/java/org/eclipse/jetty/io/ssl/SslConnection.java | 4 ++-- ...Test.java => DefaultRetainableByteBufferPoolTest.java} | 4 ++-- .../java/org/eclipse/jetty/server/AbstractConnector.java | 6 +++--- .../java/org/eclipse/jetty/server/HttpConnection.java | 4 ++-- .../org/eclipse/jetty/server/SslConnectionFactory.java | 4 ++-- .../websocket/core/client/CoreClientUpgradeRequest.java | 4 ++-- .../websocket/core/server/internal/RFC6455Handshaker.java | 4 ++-- .../websocket/core/server/internal/RFC8441Handshaker.java | 4 ++-- 15 files changed, 33 insertions(+), 33 deletions(-) rename jetty-io/src/main/java/org/eclipse/jetty/io/{RetainableByteBufferPool.java => DefaultRetainableByteBufferPool.java} (95%) rename jetty-io/src/test/java/org/eclipse/jetty/io/{RetainableByteBufferPoolTest.java => DefaultRetainableByteBufferPoolTest.java} (93%) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 87fe1befc296..ff44818d4322 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -58,7 +58,7 @@ import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.MappedByteBufferPool; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Jetty; @@ -203,8 +203,8 @@ protected void doStart() throws Exception Scheduler scheduler = getScheduler(); if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); - if (getBean(RetainableByteBufferPool.class) == null) - addBean(new RetainableByteBufferPool(0, 2048, 65536, ProcessorUtils.availableProcessors() * 2)); + if (getBean(DefaultRetainableByteBufferPool.class) == null) + addBean(new DefaultRetainableByteBufferPool(0, 2048, 65536, ProcessorUtils.availableProcessors() * 2)); if (resolver == null) setSocketAddressResolver(new SocketAddressResolver.Async(getExecutor(), getScheduler(), getAddressResolutionTimeout())); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 1f43fb22e1d3..3bec4d403b28 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -32,7 +32,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; @@ -64,7 +64,7 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } - this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); + this.retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); } @Override diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index 9bb6b2e953a4..f5ac6733ee2f 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -47,7 +47,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -85,7 +85,7 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr requests.addLast(0); HttpClient client = destination.getHttpClient(); - this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); + this.retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); } public HttpDestination getHttpDestination() diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index a2da424d754a..d420a1dcbcc3 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -70,7 +70,7 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, byteBufferPool); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, byteBufferPool); HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 0181274655ef..6673c1d658f1 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -39,7 +39,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; @@ -282,7 +282,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java index 6e3cb418cca2..2c67e29fdc6a 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ByteBufferPool.java @@ -30,7 +30,7 @@ * if they are released, they may be recycled and reused, otherwise they will be garbage * collected as usual.

*/ -public interface ByteBufferPool extends MemoryPool +public interface ByteBufferPool { /** *

Requests a {@link ByteBuffer} of the given size.

diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java similarity index 95% rename from jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java rename to jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index c288b517d40b..96b244c07095 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -24,19 +24,19 @@ import org.eclipse.jetty.util.component.Container; @ManagedObject -public class RetainableByteBufferPool implements MemoryPool +public class DefaultRetainableByteBufferPool implements MemoryPool { private final Pool[] _direct; private final Pool[] _indirect; private final int _factor; private final int _minCapacity; - public RetainableByteBufferPool() + public DefaultRetainableByteBufferPool() { this(0, 1024, 65536, Integer.MAX_VALUE); } - public RetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) + public DefaultRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) { _factor = factor <= 0 ? 1024 : factor; if (minCapacity <= 0) @@ -155,7 +155,7 @@ private long getByteBufferCount(boolean direct) */ public static MemoryPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) { - MemoryPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); + MemoryPool retainableByteBufferPool = container == null ? null : container.getBean(DefaultRetainableByteBufferPool.class); if (retainableByteBufferPool == null) retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); return retainableByteBufferPool; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 21bee928eba2..8c49a537bf1c 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -37,7 +37,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -192,7 +192,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { - this(RetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + this(DefaultRetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } public SslConnection(MemoryPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java similarity index 93% rename from jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java rename to jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index 12ff9f04490c..50a72c816a87 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/RetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -19,12 +19,12 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.Is.is; -public class RetainableByteBufferPoolTest +public class DefaultRetainableByteBufferPoolTest { @Test public void testAcquireRelease() { - RetainableByteBufferPool pool = new RetainableByteBufferPool(); + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); for (int i = 0; i < 10; i++) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 9a5d3b56a0f6..abb60663ad52 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.StringUtil; @@ -189,8 +189,8 @@ public AbstractConnector( pool = _server.getBean(ByteBufferPool.class); _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); - RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); - addBean(retainableByteBufferPool == null ? new RetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); + DefaultRetainableByteBufferPool retainableByteBufferPool = _server.getBean(DefaultRetainableByteBufferPool.class); + addBean(retainableByteBufferPool == null ? new DefaultRetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); addEventListener(new Container.Listener() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index fc115f0e97ba..0e9a42fb1651 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -36,7 +36,7 @@ import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -98,7 +98,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, _bufferPool);; + _retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, _bufferPool);; _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index b5c8f2da1b87..7792d82ecc69 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -24,7 +24,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.util.annotation.Name; @@ -165,7 +165,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 8cbd29e87fa8..6fe026118ccb 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -41,7 +41,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -450,7 +450,7 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index 825937f4c39f..d1ff8cbf7901 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -26,7 +26,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -99,7 +99,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index c2f833a0eb83..8e56e42f7a6b 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -23,7 +23,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.MemoryPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.RetainableByteBufferPool; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -83,7 +83,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } From 9557f469a2e7c252efd2ab5a224f412639512648 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 09:26:57 +0200 Subject: [PATCH 36/75] rename Signed-off-by: Ludovic Orban --- .../client/http/HttpReceiverOverHTTP.java | 4 +-- .../client/http/HttpConnectionOverFCGI.java | 4 +-- .../client/HTTP2ClientConnectionFactory.java | 9 +++--- .../eclipse/jetty/http2/HTTP2Connection.java | 6 ++-- .../AbstractHTTP2ServerConnectionFactory.java | 5 ++-- .../http2/server/HTTP2ServerConnection.java | 5 ++-- .../io/DefaultRetainableByteBufferPool.java | 28 +++++-------------- ...ool.java => RetainableByteBufferPool.java} | 18 ++++-------- .../eclipse/jetty/io/ssl/SslConnection.java | 6 ++-- .../DefaultRetainableByteBufferPoolTest.java | 10 +++---- .../eclipse/jetty/server/HttpConnection.java | 6 ++-- .../jetty/server/SslConnectionFactory.java | 5 ++-- .../core/client/CoreClientUpgradeRequest.java | 7 ++--- .../core/internal/WebSocketConnection.java | 8 +++--- .../server/internal/AbstractHandshaker.java | 5 ++-- .../server/internal/RFC6455Handshaker.java | 5 ++-- .../server/internal/RFC8441Handshaker.java | 7 ++--- 17 files changed, 54 insertions(+), 84 deletions(-) rename jetty-io/src/main/java/org/eclipse/jetty/io/{MemoryPool.java => RetainableByteBufferPool.java} (61%) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 3bec4d403b28..e3724f6e5252 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -30,7 +30,7 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; @@ -44,7 +44,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private final LongAdder inMessages = new LongAdder(); private final HttpParser parser; - private final MemoryPool retainableByteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private RetainableByteBuffer networkBuffer; private boolean shutdown; private boolean complete; diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index f5ac6733ee2f..a365d5984a82 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -45,7 +45,7 @@ import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; @@ -72,7 +72,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne private final ClientParser parser; private RetainableByteBuffer networkBuffer; private Object attachment; - private final MemoryPool retainableByteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Promise promise) { diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index d420a1dcbcc3..af9d2e237362 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -30,10 +30,9 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -70,7 +69,7 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, byteBufferPool); HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); @@ -86,7 +85,7 @@ private static class HTTP2ClientConnection extends HTTP2Connection implements Ca private final Promise promise; private final Session.Listener listener; - private HTTP2ClientConnection(HTTP2Client client, MemoryPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) + private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) { super(retainableByteBufferPool, executor, endpoint, parser, session, bufferSize); this.client = client; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index eee8161e998f..36a9d15bc189 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -25,7 +25,7 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; @@ -49,7 +49,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final Queue tasks = new ArrayDeque<>(); private final HTTP2Producer producer = new HTTP2Producer(); private final AtomicLong bytesIn = new AtomicLong(); - private final MemoryPool retainableByteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Parser parser; private final ISession session; private final int bufferSize; @@ -57,7 +57,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private boolean useInputDirectByteBuffers; private boolean useOutputDirectByteBuffers; - protected HTTP2Connection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); this.retainableByteBufferPool = retainableByteBufferPool; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 6673c1d658f1..62f1b0827260 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -37,8 +37,7 @@ import org.eclipse.jetty.http2.parser.WindowRateControl; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; @@ -282,7 +281,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index 67063fba9e98..09bd604979a3 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -46,8 +46,7 @@ import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.SettingsBodyParser; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.util.BufferUtil; @@ -88,7 +87,7 @@ public static boolean isSupportedProtocol(String protocol) private final HttpConfiguration httpConfig; private boolean recycleHttpChannels = true; - public HTTP2ServerConnection(MemoryPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) + public HTTP2ServerConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) { super(retainableByteBufferPool, executor, endPoint, parser, session, inputBufferSize); this.listener = listener; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 96b244c07095..dd05f17b9e83 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -24,7 +24,7 @@ import org.eclipse.jetty.util.component.Container; @ManagedObject -public class DefaultRetainableByteBufferPool implements MemoryPool +public class DefaultRetainableByteBufferPool implements RetainableByteBufferPool { private final Pool[] _direct; private final Pool[] _indirect; @@ -104,14 +104,6 @@ private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direc return new RetainableByteBuffer(buffer, releaser); } - @Override - public void release(RetainableByteBuffer buffer) - { - if (buffer == null) - return; - buffer.release(); - } - private Pool bucketFor(int capacity, boolean direct) { if (capacity < _minCapacity) @@ -147,15 +139,15 @@ private long getByteBufferCount(boolean direct) } /** - * Find a {@link MemoryPool} of {@link RetainableByteBuffer} implementation in the given container, or wrap the given + * Find a {@link RetainableByteBufferPool} implementation in the given container, or wrap the given * {@link ByteBufferPool} with an adapter. * @param container the container to search for an existing memory pool. * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. - * @return the memory pool found or the wrapped one. + * @return the {@link RetainableByteBufferPool} found or the wrapped one. */ - public static MemoryPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) + public static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) { - MemoryPool retainableByteBufferPool = container == null ? null : container.getBean(DefaultRetainableByteBufferPool.class); + RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(DefaultRetainableByteBufferPool.class); if (retainableByteBufferPool == null) retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); return retainableByteBufferPool; @@ -163,9 +155,9 @@ public static MemoryPool findOrAdapt(Container container, /** * An adapter class which exposes a {@link ByteBufferPool} as a - * {@link MemoryPool} of {@link RetainableByteBuffer}. + * {@link RetainableByteBufferPool}. */ - private static class AdapterMemoryPool implements MemoryPool + private static class AdapterMemoryPool implements RetainableByteBufferPool { private final ByteBufferPool byteBufferPool; private final Consumer releaser; @@ -182,11 +174,5 @@ public RetainableByteBuffer acquire(int size, boolean direct) ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); return new RetainableByteBuffer(byteBuffer, releaser); } - - @Override - public void release(RetainableByteBuffer buffer) - { - buffer.release(); - } } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java similarity index 61% rename from jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java rename to jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 5a8704c00737..e407d410da6c 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/MemoryPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -13,14 +13,12 @@ package org.eclipse.jetty.io; -import org.eclipse.jetty.util.component.Container; - /** - * A Pool of objects representing memory that can be acquired based on size and direction. The held instances may be the memory - * component itself (e.g.: {@link java.nio.ByteBuffer}) or an abstraction providing access to the memory component. - * @param The memory buffer type. + *

A {@link RetainableByteBuffer} pool.

+ *

Acquired buffers must be released by calling {@link RetainableByteBuffer#release()} otherwise the memory they hold will + * be leaked.

*/ -public interface MemoryPool +public interface RetainableByteBufferPool { /** * Acquire a memory buffer from the pool. @@ -28,11 +26,5 @@ public interface MemoryPool * @param direct true if a direct memory buffer is needed, false otherwise. * @return a memory buffer. */ - T acquire(int size, boolean direct); - - /** - * Release a previously acquired memory buffer to the pool. - * @param buffer the memory buffer to release. - */ - void release(T buffer); + RetainableByteBuffer acquire(int size, boolean direct); } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 8c49a537bf1c..3b428e3f2303 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -35,7 +35,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; @@ -110,7 +110,7 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; - private final MemoryPool _retainableByteBufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; @@ -195,7 +195,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint this(DefaultRetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } - public SslConnection(MemoryPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index 50a72c816a87..d4616b3d6fd1 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -33,23 +33,23 @@ public void testAcquireRelease() assertThat(buffer, is(notNullValue())); RetainableByteBuffer buffer2 = pool.acquire(10, true); assertThat(buffer2, is(notNullValue())); - pool.release(buffer); - pool.release(buffer2); + buffer.release(); + buffer2.release(); } { RetainableByteBuffer buffer = pool.acquire(16385, true); assertThat(buffer, is(notNullValue())); - pool.release(buffer); + buffer.release(); } { RetainableByteBuffer buffer = pool.acquire(32768, true); assertThat(buffer, is(notNullValue())); - pool.release(buffer); + buffer.release(); } { RetainableByteBuffer buffer = pool.acquire(32768, false); assertThat(buffer, is(notNullValue())); - pool.release(buffer); + buffer.release(); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 0e9a42fb1651..e6b4c56a3e41 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -34,7 +34,7 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; @@ -58,7 +58,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpConfiguration _config; private final Connector _connector; private final ByteBufferPool _bufferPool; - private final MemoryPool _retainableByteBufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final HttpInput _input; private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; @@ -98,7 +98,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, _bufferPool);; + _retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, _bufferPool); _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 7792d82ecc69..23c6feb753bb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -22,8 +22,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; @@ -165,7 +164,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 6fe026118ccb..35592d09c1a1 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -38,10 +38,9 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -450,7 +449,7 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index eaceb90a380e..54565344fb2e 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -26,7 +26,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -53,7 +53,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private final AutoLock lock = new AutoLock(); private final ByteBufferPool bufferPool; - private final MemoryPool retainableByteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Generator generator; private final Parser parser; private final WebSocketCoreSession coreSession; @@ -79,7 +79,7 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, - MemoryPool retainableByteBufferPool, + RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { this(endp, executor, scheduler, bufferPool, retainableByteBufferPool, coreSession, null); @@ -103,7 +103,7 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, - MemoryPool retainableByteBufferPool, + RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession, Random randomMask) { diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index 5dc951564c5f..734091f247e9 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -24,8 +24,7 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpTransport; @@ -218,7 +217,7 @@ protected void handle(Runnable runnable) protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession); - protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, MemoryPool retainableByteBufferPool, WebSocketCoreSession coreSession) + protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index d1ff8cbf7901..f37c7cfeaa7d 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -24,8 +24,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; @@ -99,7 +98,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index 8e56e42f7a6b..c2726e523fb5 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -20,10 +20,9 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.MemoryPool; -import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -83,7 +82,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - MemoryPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } From 378bfe7eb37b1429aea9b2aca0e6aa6b9eae7d03 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 09:40:42 +0200 Subject: [PATCH 37/75] change bean lookup Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/client/HttpClient.java | 13 +++++++------ .../jetty/io/DefaultRetainableByteBufferPool.java | 2 +- .../org/eclipse/jetty/server/AbstractConnector.java | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index ff44818d4322..4a1e0305992e 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -59,6 +59,7 @@ import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Jetty; @@ -194,17 +195,17 @@ protected void doStart() throws Exception threadPool.setName(name); setExecutor(threadPool); } + int maxBucketSize = executor instanceof ThreadPool.SizedThreadPool + ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 + : ProcessorUtils.availableProcessors() * 2; ByteBufferPool byteBufferPool = getByteBufferPool(); if (byteBufferPool == null) - setByteBufferPool(new MappedByteBufferPool(2048, - executor instanceof ThreadPool.SizedThreadPool - ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 - : ProcessorUtils.availableProcessors() * 2)); + setByteBufferPool(new MappedByteBufferPool(2048, maxBucketSize)); + if (getBean(RetainableByteBufferPool.class) == null) + addBean(new DefaultRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); Scheduler scheduler = getScheduler(); if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); - if (getBean(DefaultRetainableByteBufferPool.class) == null) - addBean(new DefaultRetainableByteBufferPool(0, 2048, 65536, ProcessorUtils.availableProcessors() * 2)); if (resolver == null) setSocketAddressResolver(new SocketAddressResolver.Async(getExecutor(), getScheduler(), getAddressResolutionTimeout())); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index dd05f17b9e83..6edb48c8ecbe 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -147,7 +147,7 @@ private long getByteBufferCount(boolean direct) */ public static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) { - RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(DefaultRetainableByteBufferPool.class); + RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); if (retainableByteBufferPool == null) retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); return retainableByteBufferPool; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index abb60663ad52..6161997f58d7 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -35,6 +35,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.StringUtil; @@ -189,7 +190,7 @@ public AbstractConnector( pool = _server.getBean(ByteBufferPool.class); _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); - DefaultRetainableByteBufferPool retainableByteBufferPool = _server.getBean(DefaultRetainableByteBufferPool.class); + RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); addBean(retainableByteBufferPool == null ? new DefaultRetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); addEventListener(new Container.Listener() From 435b6bbf23531bc3a955744c3d92dddf839d70d1 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 10:02:37 +0200 Subject: [PATCH 38/75] fix checkstyle Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/client/HttpClient.java | 2 +- .../org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java | 4 ++-- .../jetty/fcgi/client/http/HttpConnectionOverFCGI.java | 4 ++-- .../main/java/org/eclipse/jetty/http2/HTTP2Connection.java | 2 +- .../http2/server/AbstractHTTP2ServerConnectionFactory.java | 2 +- .../src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java | 4 ++-- .../main/java/org/eclipse/jetty/server/AbstractConnector.java | 2 +- .../main/java/org/eclipse/jetty/server/HttpConnection.java | 4 ++-- .../java/org/eclipse/jetty/server/SslConnectionFactory.java | 2 +- .../jetty/websocket/core/internal/WebSocketConnection.java | 2 +- .../websocket/core/server/internal/RFC6455Handshaker.java | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 4a1e0305992e..022cff6e38be 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -57,8 +57,8 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index e3724f6e5252..1c84bc8519df 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -29,10 +29,10 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index a365d5984a82..c876c2e370e5 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -44,10 +44,10 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 36a9d15bc189..a825f553ad45 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -25,8 +25,8 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 62f1b0827260..99b3fda859b9 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -36,9 +36,9 @@ import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.WindowRateControl; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 3b428e3f2303..4f0ba422cb0f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -34,10 +34,10 @@ import org.eclipse.jetty.io.AbstractEndPoint; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 6161997f58d7..76ec5d434258 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -33,8 +33,8 @@ import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.ProcessorUtils; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index e6b4c56a3e41..f5228dffc4a9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -32,11 +32,11 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index 23c6feb753bb..e942c918174a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -21,9 +21,9 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.util.annotation.Name; diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 54565344fb2e..94500db1fc9e 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -26,8 +26,8 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.component.Dumpable; diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index f37c7cfeaa7d..d96c56eb3736 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -24,8 +24,8 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; From d8099d6914b4db80f0a2bc59156de7ac97ad4eba Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 10:21:24 +0200 Subject: [PATCH 39/75] fix error message Signed-off-by: Ludovic Orban --- .../jetty/websocket/core/internal/WebSocketConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 94500db1fc9e..5c412af037ec 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -113,7 +113,7 @@ public WebSocketConnection(EndPoint endp, Objects.requireNonNull(coreSession, "Session"); Objects.requireNonNull(executor, "Executor"); Objects.requireNonNull(bufferPool, "ByteBufferPool"); - Objects.requireNonNull(retainableByteBufferPool, "MemoryPool"); + Objects.requireNonNull(retainableByteBufferPool, "RetainableByteBufferPool"); this.bufferPool = bufferPool; this.retainableByteBufferPool = retainableByteBufferPool; From fb8c215255d9f5c6c10c8b734f2a1f762fa40375 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 17:37:32 +0200 Subject: [PATCH 40/75] add memory calculation + clear + tests Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 44 +++++++++++++++++++ .../DefaultRetainableByteBufferPoolTest.java | 42 +++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 6edb48c8ecbe..59b972c4ac7b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.util.Pool; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.component.Container; @ManagedObject @@ -138,6 +139,49 @@ private long getByteBufferCount(boolean direct) return Arrays.stream(buckets).mapToLong(Pool::size).sum(); } + @ManagedAttribute("The bytes retained by direct ByteBuffers") + public long getDirectMemory() + { + return getMemory(true); + } + + @ManagedAttribute("The bytes retained by heap ByteBuffers") + public long getHeapMemory() + { + return getMemory(false); + } + + private long getMemory(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + long total = 0L; + for (int i = 0; i < buckets.length; i++) + { + Pool bucket = buckets[i]; + long capacity = (i + 1L) * _factor; + total += bucket.size() * capacity; + } + return total; + } + + @ManagedOperation(value = "Clears this RetainableByteBufferPool", impact = "ACTION") + public void clear() + { + clearArray(_direct); + clearArray(_indirect); + } + + private void clearArray(Pool[] poolArray) + { + for (Pool retainableByteBufferPool : poolArray) + { + for (Pool.Entry entry : retainableByteBufferPool.values()) + { + retainableByteBufferPool.remove(entry); + } + } + } + /** * Find a {@link RetainableByteBufferPool} implementation in the given container, or wrap the given * {@link ByteBufferPool} with an adapter. diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index d4616b3d6fd1..b01cfa5cf0e4 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -21,12 +21,43 @@ public class DefaultRetainableByteBufferPoolTest { + @Test + public void testFactorAndCapacity() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + pool.acquire(1, true); // not pooled, < minCapacity + pool.acquire(10, true); // pooled + pool.acquire(20, true); // pooled + pool.acquire(30, true); // not pooled, > maxCapacity + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + } + + @Test + public void testClearUnlinksLeakedBuffers() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); + + pool.acquire(10, true); + pool.acquire(10, true); + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(2048L)); + + pool.clear(); + + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + } + @Test public void testAcquireRelease() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); - for (int i = 0; i < 10; i++) + for (int i = 0; i < 3; i++) { { RetainableByteBuffer buffer = pool.acquire(10, true); @@ -55,5 +86,14 @@ public void testAcquireRelease() assertThat(pool.getDirectByteBufferCount(), is(4L)); assertThat(pool.getHeapByteBufferCount(), is(1L)); + assertThat(pool.getDirectMemory(), is(52224L)); + assertThat(pool.getHeapMemory(), is(32768L)); + + pool.clear(); + + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getHeapByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + assertThat(pool.getHeapMemory(), is(0L)); } } From f06f511208a0f27ea09a547b5345dd080514e4ce Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 17:54:50 +0200 Subject: [PATCH 41/75] add available memory calculation + tests Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 45 ++++++++++++++++ .../DefaultRetainableByteBufferPoolTest.java | 54 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 59b972c4ac7b..aa2cc7669f43 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -16,6 +16,8 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.ToLongFunction; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; @@ -139,6 +141,24 @@ private long getByteBufferCount(boolean direct) return Arrays.stream(buckets).mapToLong(Pool::size).sum(); } + @ManagedAttribute("The number of pooled direct ByteBuffers that are available") + public long getAvailableDirectByteBufferCount() + { + return getAvailableByteBufferCount(true); + } + + @ManagedAttribute("The number of pooled heap ByteBuffers that are available") + public long getAvailableHeapByteBufferCount() + { + return getAvailableByteBufferCount(false); + } + + private long getAvailableByteBufferCount(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(pool -> pool.values().stream().filter(Pool.Entry::isIdle).count()).sum(); + } + @ManagedAttribute("The bytes retained by direct ByteBuffers") public long getDirectMemory() { @@ -164,6 +184,31 @@ private long getMemory(boolean direct) return total; } + @ManagedAttribute("The available bytes retained by direct ByteBuffers") + public long getAvailableDirectMemory() + { + return getAvailableMemory(true); + } + + @ManagedAttribute("The available bytes retained by heap ByteBuffers") + public long getAvailableHeapMemory() + { + return getAvailableMemory(false); + } + + private long getAvailableMemory(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + long total = 0L; + for (int i = 0; i < buckets.length; i++) + { + Pool bucket = buckets[i]; + long capacity = (i + 1L) * _factor; + total += bucket.values().stream().filter(Pool.Entry::isIdle).count() * capacity; + } + return total; + } + @ManagedOperation(value = "Clears this RetainableByteBufferPool", impact = "ACTION") public void clear() { diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index b01cfa5cf0e4..3559e7a50a16 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -13,6 +13,9 @@ package org.eclipse.jetty.io; +import java.util.ArrayList; +import java.util.List; + import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -21,6 +24,51 @@ public class DefaultRetainableByteBufferPoolTest { + @Test + public void testMaxBucketSize() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, 2); + + pool.acquire(1, true); // pooled + pool.acquire(1, true); // pooled + pool.acquire(1, true); // not pooled, bucket is full + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(20L)); + + pool.acquire(11, true); // pooled + pool.acquire(11, true); // pooled + pool.acquire(11, true); // not pooled, bucket is full + + assertThat(pool.getDirectByteBufferCount(), is(4L)); + assertThat(pool.getDirectMemory(), is(60L)); + } + + @Test + public void testBufferReleaseRepools() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, 1); + + List all = new ArrayList<>(); + + all.add(pool.acquire(1, true)); // pooled + all.add(pool.acquire(1, true)); // not pooled, bucket is full + all.add(pool.acquire(11, true)); // pooled + all.add(pool.acquire(11, true)); // not pooled, bucket is full + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + + all.forEach(RetainableByteBuffer::release); + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(2L)); + assertThat(pool.getAvailableDirectMemory(), is(30L)); + } + @Test public void testFactorAndCapacity() { @@ -33,6 +81,8 @@ public void testFactorAndCapacity() assertThat(pool.getDirectByteBufferCount(), is(2L)); assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); } @Test @@ -45,11 +95,15 @@ public void testClearUnlinksLeakedBuffers() assertThat(pool.getDirectByteBufferCount(), is(2L)); assertThat(pool.getDirectMemory(), is(2048L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); pool.clear(); assertThat(pool.getDirectByteBufferCount(), is(0L)); assertThat(pool.getDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); } @Test From 314009104edcc4853ca60c22f84da48727beaa17 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Mon, 21 Jun 2021 17:59:57 +0200 Subject: [PATCH 42/75] rename Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/DefaultRetainableByteBufferPool.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index aa2cc7669f43..62255643eac7 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -16,8 +16,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.ToLongFunction; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; @@ -238,7 +236,7 @@ public static RetainableByteBufferPool findOrAdapt(Container container, ByteBuff { RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); if (retainableByteBufferPool == null) - retainableByteBufferPool = new AdapterMemoryPool(byteBufferPool); + retainableByteBufferPool = new ByteBufferToRetainableByteBufferAdapterPool(byteBufferPool); return retainableByteBufferPool; } @@ -246,12 +244,12 @@ public static RetainableByteBufferPool findOrAdapt(Container container, ByteBuff * An adapter class which exposes a {@link ByteBufferPool} as a * {@link RetainableByteBufferPool}. */ - private static class AdapterMemoryPool implements RetainableByteBufferPool + private static class ByteBufferToRetainableByteBufferAdapterPool implements RetainableByteBufferPool { private final ByteBufferPool byteBufferPool; private final Consumer releaser; - public AdapterMemoryPool(ByteBufferPool byteBufferPool) + public ByteBufferToRetainableByteBufferAdapterPool(ByteBufferPool byteBufferPool) { this.byteBufferPool = byteBufferPool; this.releaser = byteBufferPool::release; From cb727bab498502bd9b23124f3d4f5ec3ad15f6da Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 7 Jul 2021 15:40:54 +0200 Subject: [PATCH 43/75] add buffer eviction mechanism Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 120 +++++++++++++++++- .../jetty/io/RetainableByteBuffer.java | 10 ++ .../DefaultRetainableByteBufferPoolTest.java | 65 ++++++++++ 3 files changed, 194 insertions(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 62255643eac7..84528a0936d1 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -23,23 +23,36 @@ import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.component.Container; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @ManagedObject public class DefaultRetainableByteBufferPool implements RetainableByteBufferPool { + private static final Logger LOG = LoggerFactory.getLogger(DefaultRetainableByteBufferPool.class); + private final Pool[] _direct; private final Pool[] _indirect; private final int _factor; private final int _minCapacity; + private final long _maxHeapMemory; + private final long _maxDirectMemory; public DefaultRetainableByteBufferPool() { - this(0, 1024, 65536, Integer.MAX_VALUE); + this(0, 1024, 65536, Integer.MAX_VALUE, -1L, -1L); } public DefaultRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) + { + this(minCapacity, factor, maxCapacity, maxBucketSize, -1L, -1L); + } + + public DefaultRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { _factor = factor <= 0 ? 1024 : factor; + this._maxHeapMemory = maxHeapMemory; + this._maxDirectMemory = maxDirectMemory; if (minCapacity <= 0) minCapacity = 0; _minCapacity = minCapacity; @@ -84,6 +97,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) reservedEntry.release(); }); reservedEntry.enable(buffer, true); + releaseExcessMemory(direct); } else { @@ -225,6 +239,110 @@ private void clearArray(Pool[] poolArray) } } + private void releaseExcessMemory(boolean direct) + { + long maxMemory = direct ? _maxDirectMemory : _maxHeapMemory; + if (maxMemory > 0) + { + long excess = getMemory(direct) - maxMemory; + if (excess > 0) + evict(direct, excess); + } + } + + /** + * This eviction mechanism searches for the RetainableByteBuffers that were released the longest time ago. + * @param direct true to search in the direct buffers buckets, false to search in the heap buffers buckets. + * @param excess the amount of bytes to evict. At least this much will be removed from the buckets. + */ + private void evict(boolean direct, long excess) + { + if (LOG.isDebugEnabled()) + LOG.debug("evicting {} bytes from {} pools", excess, (direct ? "direct" : "heap")); + long totalMemoryCleared = 0L; + long now = System.nanoTime(); + + Pool[] buckets = direct ? _direct : _indirect; + @SuppressWarnings("unchecked") + Pool.Entry[] oldestEntries = new Pool.Entry[buckets.length]; + + for (int i = 0; i < buckets.length; i++) + { + Pool bucket = buckets[i]; + oldestEntries[i] = findOldestEntry(now, bucket); + } + + while (true) + { + int oldestEntryBucketIndex = -1; + Pool.Entry oldestEntry = null; + + for (int i = 0; i < oldestEntries.length; i++) + { + Pool.Entry entry = oldestEntries[i]; + if (entry == null) + continue; + + if (oldestEntry != null) + { + long entryAge = now - entry.getPooled().getLastUpdate(); + if (LOG.isDebugEnabled()) + LOG.debug("checking entry of capacity {} and age {} vs entry of capacity {} and age {}", entry.getPooled().capacity(), now - entry.getPooled().getLastUpdate(), oldestEntry.getPooled().capacity(), now - oldestEntry.getPooled().getLastUpdate()); + if (entryAge > now - oldestEntry.getPooled().getLastUpdate()) + { + oldestEntry = entry; + oldestEntryBucketIndex = i; + } + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("checking entry of capacity {} and age {}", entry.getPooled().capacity(), now - entry.getPooled().getLastUpdate()); + oldestEntry = entry; + oldestEntryBucketIndex = i; + } + } + + if (oldestEntry != null) + { + if (LOG.isDebugEnabled()) + LOG.debug("removing entry of capacity {} and age {}", oldestEntry.getPooled().capacity(), now - oldestEntry.getPooled().getLastUpdate()); + oldestEntry.remove(); + oldestEntries[oldestEntryBucketIndex] = findOldestEntry(now, buckets[oldestEntryBucketIndex]); + totalMemoryCleared += oldestEntry.getPooled().capacity(); + if (totalMemoryCleared >= excess) + break; + } + else + { + if (LOG.isDebugEnabled()) + LOG.debug("found no entry to remove"); + break; + } + } + if (LOG.isDebugEnabled()) + LOG.debug("eviction done, cleared {} bytes from {} pools", totalMemoryCleared, (direct ? "direct" : "heap")); + } + + private Pool.Entry findOldestEntry(long now, Pool bucket) + { + Pool.Entry oldestEntry = null; + for (Pool.Entry entry : bucket.values()) + { + if (oldestEntry != null) + { + long entryAge = now - entry.getPooled().getLastUpdate(); + if (entryAge > now - oldestEntry.getPooled().getLastUpdate()) + oldestEntry = entry; + } + else + { + oldestEntry = entry; + } + } + return oldestEntry; + } + /** * Find a {@link RetainableByteBufferPool} implementation in the given container, or wrap the given * {@link ByteBufferPool} with an adapter. diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 879040b1cafe..50c1923f617b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -15,6 +15,7 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import org.eclipse.jetty.util.BufferUtil; @@ -31,6 +32,7 @@ public class RetainableByteBuffer implements Retainable private final ByteBuffer buffer; private final AtomicInteger references; private final Consumer releaser; + private final AtomicLong lastUpdate = new AtomicLong(System.nanoTime()); RetainableByteBuffer(ByteBuffer buffer, Consumer releaser) { @@ -49,6 +51,11 @@ public ByteBuffer getBuffer() return buffer; } + public long getLastUpdate() + { + return lastUpdate.getOpaque(); + } + public int getReferences() { return references.get(); @@ -69,7 +76,10 @@ public int release() { int ref = references.decrementAndGet(); if (ref == 0) + { + lastUpdate.setOpaque(System.nanoTime()); releaser.accept(buffer); + } else if (ref < 0) throw new IllegalStateException("already released " + this); return ref; diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index 3559e7a50a16..ba6d69aa35d8 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -24,6 +24,71 @@ public class DefaultRetainableByteBufferPoolTest { + @Test + public void testSimpleMaxMemoryEviction() throws Exception + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 30, 30); + + RetainableByteBuffer buf1 = pool.acquire(1, true); + Thread.sleep(10); + RetainableByteBuffer buf2 = pool.acquire(1, true); + Thread.sleep(10); + RetainableByteBuffer buf3 = pool.acquire(11, true); + + buf1.release(); + buf2.release(); + buf3.release(); + + // Make sure buf1 got evicted as it is the oldest one. + + assertThat(pool.getAvailableDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + + RetainableByteBuffer buf4 = pool.acquire(1, true); + assertThat(buf4 == buf2, is(true)); + RetainableByteBuffer buf5 = pool.acquire(11, true); + assertThat(buf5 == buf3, is(true)); + } + + @Test + public void testMaxMemoryEviction() throws Exception + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 30, 30); + + RetainableByteBuffer buf1 = pool.acquire(1, false); + Thread.sleep(10); + RetainableByteBuffer buf2 = pool.acquire(1, false); + Thread.sleep(10); + RetainableByteBuffer buf3 = pool.acquire(11, false); // evicts buf1 + Thread.sleep(10); + + buf3.release(); + Thread.sleep(10); + buf2.release(); + Thread.sleep(10); + buf1.release(); + + RetainableByteBuffer buf4 = pool.acquire(1, false); // == buf2 + RetainableByteBuffer buf5 = pool.acquire(11, false); // == buf3 + RetainableByteBuffer buf6 = pool.acquire(11, false); // Evicts buf3 as it was the one released the longest ago. + + buf4.release(); + buf5.release(); + buf6.release(); + + // Make sure buf1 and buf3 got evicted and only buf2 and buf6 are left. + + assertThat(pool.getAvailableHeapByteBufferCount(), is(2L)); + assertThat(pool.getHeapByteBufferCount(), is(2L)); + assertThat(pool.getHeapMemory(), is(30L)); + + RetainableByteBuffer buf7 = pool.acquire(1, false); + assertThat(buf7 == buf2, is(true)); + RetainableByteBuffer buf8 = pool.acquire(11, false); + assertThat(buf8 == buf6, is(true)); + } + @Test public void testMaxBucketSize() { From 13f152d76b5a48ee812d9d98afc3ba80e4ba33f3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Wed, 7 Jul 2021 15:50:10 +0200 Subject: [PATCH 44/75] use atomics to keep track of memory Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 84528a0936d1..25e02035a610 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -15,6 +15,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import org.eclipse.jetty.util.BufferUtil; @@ -37,6 +38,8 @@ public class DefaultRetainableByteBufferPool implements RetainableByteBufferPool private final int _minCapacity; private final long _maxHeapMemory; private final long _maxDirectMemory; + private final AtomicLong _currentHeapMemory = new AtomicLong(); + private final AtomicLong _currentDirectMemory = new AtomicLong(); public DefaultRetainableByteBufferPool() { @@ -97,6 +100,10 @@ public RetainableByteBuffer acquire(int size, boolean direct) reservedEntry.release(); }); reservedEntry.enable(buffer, true); + if (direct) + _currentDirectMemory.addAndGet(buffer.capacity()); + else + _currentHeapMemory.addAndGet(buffer.capacity()); releaseExcessMemory(direct); } else @@ -185,15 +192,10 @@ public long getHeapMemory() private long getMemory(boolean direct) { - Pool[] buckets = direct ? _direct : _indirect; - long total = 0L; - for (int i = 0; i < buckets.length; i++) - { - Pool bucket = buckets[i]; - long capacity = (i + 1L) * _factor; - total += bucket.size() * capacity; - } - return total; + if (direct) + return _currentDirectMemory.get(); + else + return _currentHeapMemory.get(); } @ManagedAttribute("The available bytes retained by direct ByteBuffers") @@ -224,17 +226,18 @@ private long getAvailableMemory(boolean direct) @ManagedOperation(value = "Clears this RetainableByteBufferPool", impact = "ACTION") public void clear() { - clearArray(_direct); - clearArray(_indirect); + clearArray(_direct, _currentDirectMemory); + clearArray(_indirect, _currentHeapMemory); } - private void clearArray(Pool[] poolArray) + private void clearArray(Pool[] poolArray, AtomicLong memoryCounter) { for (Pool retainableByteBufferPool : poolArray) { for (Pool.Entry entry : retainableByteBufferPool.values()) { - retainableByteBufferPool.remove(entry); + entry.remove(); + memoryCounter.addAndGet(-entry.getPooled().capacity()); } } } @@ -308,6 +311,10 @@ private void evict(boolean direct, long excess) if (LOG.isDebugEnabled()) LOG.debug("removing entry of capacity {} and age {}", oldestEntry.getPooled().capacity(), now - oldestEntry.getPooled().getLastUpdate()); oldestEntry.remove(); + if (direct) + _currentDirectMemory.addAndGet(-oldestEntry.getPooled().capacity()); + else + _currentHeapMemory.addAndGet(-oldestEntry.getPooled().capacity()); oldestEntries[oldestEntryBucketIndex] = findOldestEntry(now, buckets[oldestEntryBucketIndex]); totalMemoryCleared += oldestEntry.getPooled().capacity(); if (totalMemoryCleared >= excess) From 2378c09bc75998a6e293a92e8aae2faae4f03e7a Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 09:12:44 +0200 Subject: [PATCH 45/75] simplify buffer eviction Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 69 ++------ .../DefaultRetainableByteBufferPoolTest.java | 157 ++++++++++-------- 2 files changed, 107 insertions(+), 119 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 25e02035a610..95bb9086c6f6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -262,73 +262,34 @@ private void evict(boolean direct, long excess) { if (LOG.isDebugEnabled()) LOG.debug("evicting {} bytes from {} pools", excess, (direct ? "direct" : "heap")); - long totalMemoryCleared = 0L; long now = System.nanoTime(); + long totalClearedCapacity = 0L; Pool[] buckets = direct ? _direct : _indirect; - @SuppressWarnings("unchecked") - Pool.Entry[] oldestEntries = new Pool.Entry[buckets.length]; - - for (int i = 0; i < buckets.length; i++) - { - Pool bucket = buckets[i]; - oldestEntries[i] = findOldestEntry(now, bucket); - } - while (true) + while (totalClearedCapacity < excess) { - int oldestEntryBucketIndex = -1; - Pool.Entry oldestEntry = null; - - for (int i = 0; i < oldestEntries.length; i++) + for (Pool bucket : buckets) { - Pool.Entry entry = oldestEntries[i]; - if (entry == null) + Pool.Entry oldestEntry = findOldestEntry(now, bucket); + if (oldestEntry == null) continue; - if (oldestEntry != null) + if (oldestEntry.remove()) { - long entryAge = now - entry.getPooled().getLastUpdate(); - if (LOG.isDebugEnabled()) - LOG.debug("checking entry of capacity {} and age {} vs entry of capacity {} and age {}", entry.getPooled().capacity(), now - entry.getPooled().getLastUpdate(), oldestEntry.getPooled().capacity(), now - oldestEntry.getPooled().getLastUpdate()); - if (entryAge > now - oldestEntry.getPooled().getLastUpdate()) - { - oldestEntry = entry; - oldestEntryBucketIndex = i; - } - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("checking entry of capacity {} and age {}", entry.getPooled().capacity(), now - entry.getPooled().getLastUpdate()); - oldestEntry = entry; - oldestEntryBucketIndex = i; + int clearedCapacity = oldestEntry.getPooled().capacity(); + if (direct) + _currentDirectMemory.addAndGet(-clearedCapacity); + else + _currentHeapMemory.addAndGet(-clearedCapacity); + totalClearedCapacity += clearedCapacity; } - } - - if (oldestEntry != null) - { - if (LOG.isDebugEnabled()) - LOG.debug("removing entry of capacity {} and age {}", oldestEntry.getPooled().capacity(), now - oldestEntry.getPooled().getLastUpdate()); - oldestEntry.remove(); - if (direct) - _currentDirectMemory.addAndGet(-oldestEntry.getPooled().capacity()); - else - _currentHeapMemory.addAndGet(-oldestEntry.getPooled().capacity()); - oldestEntries[oldestEntryBucketIndex] = findOldestEntry(now, buckets[oldestEntryBucketIndex]); - totalMemoryCleared += oldestEntry.getPooled().capacity(); - if (totalMemoryCleared >= excess) - break; - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("found no entry to remove"); - break; + // else a concurrent thread evicted the same entry -> do not account for its capacity. } } + if (LOG.isDebugEnabled()) - LOG.debug("eviction done, cleared {} bytes from {} pools", totalMemoryCleared, (direct ? "direct" : "heap")); + LOG.debug("eviction done, cleared {} bytes from {} pools", totalClearedCapacity, (direct ? "direct" : "heap")); } private Pool.Entry findOldestEntry(long now, Pool bucket) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index ba6d69aa35d8..e76357d93e52 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -19,74 +19,101 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; public class DefaultRetainableByteBufferPoolTest { @Test - public void testSimpleMaxMemoryEviction() throws Exception + public void testMaxMemoryEviction() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 30, 30); + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 40, 40); - RetainableByteBuffer buf1 = pool.acquire(1, true); - Thread.sleep(10); - RetainableByteBuffer buf2 = pool.acquire(1, true); - Thread.sleep(10); - RetainableByteBuffer buf3 = pool.acquire(11, true); + RetainableByteBuffer buf1 = pool.acquire(10, true); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + RetainableByteBuffer buf2 = pool.acquire(10, true); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + RetainableByteBuffer buf3 = pool.acquire(20, true); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + RetainableByteBuffer buf4 = pool.acquire(20, true); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getDirectMemory(), greaterThan(0L)); buf1.release(); buf2.release(); buf3.release(); + buf4.release(); - // Make sure buf1 got evicted as it is the oldest one. + assertThat(pool.getAvailableDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), lessThan(4L)); + assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getDirectByteBufferCount(), lessThan(4L)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + assertThat(pool.getDirectMemory(), greaterThan(0L)); + } - assertThat(pool.getAvailableDirectByteBufferCount(), is(2L)); - assertThat(pool.getDirectByteBufferCount(), is(2L)); - assertThat(pool.getDirectMemory(), is(30L)); + @Test + void testBelowMinCapacityDoesNotPool() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); - RetainableByteBuffer buf4 = pool.acquire(1, true); - assertThat(buf4 == buf2, is(true)); - RetainableByteBuffer buf5 = pool.acquire(11, true); - assertThat(buf5 == buf3, is(true)); + RetainableByteBuffer buf1 = pool.acquire(1, true); + assertThat(buf1.capacity(), is(1)); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + + buf1.release(); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); } @Test - public void testMaxMemoryEviction() throws Exception + void testOverMaxCapacityDoesNotPool() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 30, 30); + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); - RetainableByteBuffer buf1 = pool.acquire(1, false); - Thread.sleep(10); - RetainableByteBuffer buf2 = pool.acquire(1, false); - Thread.sleep(10); - RetainableByteBuffer buf3 = pool.acquire(11, false); // evicts buf1 - Thread.sleep(10); + RetainableByteBuffer buf1 = pool.acquire(21, true); + assertThat(buf1.capacity(), is(21)); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); - buf3.release(); - Thread.sleep(10); - buf2.release(); - Thread.sleep(10); buf1.release(); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + } - RetainableByteBuffer buf4 = pool.acquire(1, false); // == buf2 - RetainableByteBuffer buf5 = pool.acquire(11, false); // == buf3 - RetainableByteBuffer buf6 = pool.acquire(11, false); // Evicts buf3 as it was the one released the longest ago. + @Test + void testTooManyReleases() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); - buf4.release(); - buf5.release(); - buf6.release(); + RetainableByteBuffer buf1 = pool.acquire(10, true); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + buf1.release(); - // Make sure buf1 and buf3 got evicted and only buf2 and buf6 are left. + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); - assertThat(pool.getAvailableHeapByteBufferCount(), is(2L)); - assertThat(pool.getHeapByteBufferCount(), is(2L)); - assertThat(pool.getHeapMemory(), is(30L)); + assertThrows(IllegalStateException.class, buf1::release); - RetainableByteBuffer buf7 = pool.acquire(1, false); - assertThat(buf7 == buf2, is(true)); - RetainableByteBuffer buf8 = pool.acquire(11, false); - assertThat(buf8 == buf6, is(true)); + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); } @Test @@ -178,34 +205,34 @@ public void testAcquireRelease() for (int i = 0; i < 3; i++) { - { - RetainableByteBuffer buffer = pool.acquire(10, true); - assertThat(buffer, is(notNullValue())); - RetainableByteBuffer buffer2 = pool.acquire(10, true); - assertThat(buffer2, is(notNullValue())); - buffer.release(); - buffer2.release(); - } - { - RetainableByteBuffer buffer = pool.acquire(16385, true); - assertThat(buffer, is(notNullValue())); - buffer.release(); - } - { - RetainableByteBuffer buffer = pool.acquire(32768, true); - assertThat(buffer, is(notNullValue())); - buffer.release(); - } - { - RetainableByteBuffer buffer = pool.acquire(32768, false); - assertThat(buffer, is(notNullValue())); - buffer.release(); - } + RetainableByteBuffer buf1 = pool.acquire(10, true); + assertThat(buf1, is(notNullValue())); + assertThat(buf1.capacity(), is(1024)); + RetainableByteBuffer buf2 = pool.acquire(10, true); + assertThat(buf2, is(notNullValue())); + assertThat(buf2.capacity(), is(1024)); + buf1.release(); + buf2.release(); + + RetainableByteBuffer buf3 = pool.acquire(16385, true); + assertThat(buf3, is(notNullValue())); + assertThat(buf3.capacity(), is(16384 + 1024)); + buf3.release(); + + RetainableByteBuffer buf4 = pool.acquire(32768, true); + assertThat(buf4, is(notNullValue())); + assertThat(buf4.capacity(), is(32768)); + buf4.release(); + + RetainableByteBuffer buf5 = pool.acquire(32768, false); + assertThat(buf5, is(notNullValue())); + assertThat(buf5.capacity(), is(32768)); + buf5.release(); } assertThat(pool.getDirectByteBufferCount(), is(4L)); assertThat(pool.getHeapByteBufferCount(), is(1L)); - assertThat(pool.getDirectMemory(), is(52224L)); + assertThat(pool.getDirectMemory(), is(1024 + 1024 + 16384 + 1024 + 32768L)); assertThat(pool.getHeapMemory(), is(32768L)); pool.clear(); From 80e4c60f7380a264fcaf623144b1f1639dd65034 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 09:32:26 +0200 Subject: [PATCH 46/75] do not expose retain counter in API anymore Signed-off-by: Ludovic Orban --- .../client/http/HttpReceiverOverHTTP.java | 2 +- .../client/http/HttpConnectionOverFCGI.java | 2 +- .../eclipse/jetty/http2/HTTP2Connection.java | 10 ++--- .../io/DefaultRetainableByteBufferPool.java | 4 +- .../jetty/io/RetainableByteBuffer.java | 18 +++++--- .../DefaultRetainableByteBufferPoolTest.java | 45 ++++++++++++------- .../eclipse/jetty/server/HttpConnection.java | 8 ++-- .../core/internal/WebSocketConnection.java | 2 +- 8 files changed, 54 insertions(+), 37 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 1c84bc8519df..f104e75676b0 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -169,7 +169,7 @@ private void process() return; } - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index c876c2e370e5..e24a61535b8a 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -165,7 +165,7 @@ void process() if (parse(networkBuffer.getBuffer())) return; - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index a825f553ad45..acb3af61e12f 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -293,7 +293,7 @@ public Runnable produce() return task; // If more references than 1 (ie not just us), don't refill into buffer and risk compaction. - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); } @@ -436,9 +436,9 @@ public ByteBuffer getBuffer() return delegate.getBuffer(); } - public int getReferences() + public boolean isRetained() { - return delegate.getReferences(); + return delegate.isRetained(); } public boolean hasRemaining() @@ -446,7 +446,7 @@ public boolean hasRemaining() return delegate.hasRemaining(); } - public int release() + public boolean release() { return delegate.release(); } @@ -475,7 +475,7 @@ public void failed(Throwable failure) private void completed(Throwable failure) { - if (delegate.release() == 0) + if (delegate.release()) { if (LOG.isDebugEnabled()) LOG.debug("Released retained {}", this, failure); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 95bb9086c6f6..84776cd6a813 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -85,7 +85,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) int capacity = (bucketIndexFor(size) + 1) * _factor; Pool bucket = bucketFor(size, direct); if (bucket == null) - return newRetainableByteBuffer(capacity, direct, byteBuffer -> {}); + return newRetainableByteBuffer(size, direct, byteBuffer -> {}); Pool.Entry entry = bucket.acquire(); RetainableByteBuffer buffer; @@ -108,7 +108,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) } else { - buffer = newRetainableByteBuffer(capacity, direct, byteBuffer -> {}); + buffer = newRetainableByteBuffer(size, direct, byteBuffer -> {}); } } else diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 50c1923f617b..cd02bfdc0b8d 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -56,9 +56,9 @@ public long getLastUpdate() return lastUpdate.getOpaque(); } - public int getReferences() + public boolean isRetained() { - return references.get(); + return references.get() > 1; } public boolean isDirect() @@ -72,17 +72,21 @@ public void retain() references.incrementAndGet(); } - public int release() + public boolean release() { int ref = references.decrementAndGet(); + if (ref < 0) + { + references.incrementAndGet(); + throw new IllegalStateException("already released " + this); + } if (ref == 0) { lastUpdate.setOpaque(System.nanoTime()); releaser.accept(buffer); + return true; } - else if (ref < 0) - throw new IllegalStateException("already released " + this); - return ref; + return false; } public int remaining() @@ -108,6 +112,6 @@ public void clear() @Override public String toString() { - return String.format("%s@%x{%s,r=%d}", getClass().getSimpleName(), hashCode(), BufferUtil.toDetailString(buffer), getReferences()); + return String.format("%s@%x{%s,r=%d}", getClass().getSimpleName(), hashCode(), BufferUtil.toDetailString(buffer), references.get()); } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index e76357d93e52..c1ab36ec399d 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -33,28 +33,35 @@ public void testMaxMemoryEviction() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 40, 40); - RetainableByteBuffer buf1 = pool.acquire(10, true); + List buffers = new ArrayList<>(); + + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); - RetainableByteBuffer buf2 = pool.acquire(10, true); + buffers.add(pool.acquire(20, true)); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); - RetainableByteBuffer buf3 = pool.acquire(20, true); + buffers.add(pool.acquire(10, true)); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); - RetainableByteBuffer buf4 = pool.acquire(20, true); + buffers.add(pool.acquire(20, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); assertThat(pool.getDirectMemory(), greaterThan(0L)); - buf1.release(); - buf2.release(); - buf3.release(); - buf4.release(); + buffers.forEach(RetainableByteBuffer::release); assertThat(pool.getAvailableDirectByteBufferCount(), greaterThan(0L)); - assertThat(pool.getAvailableDirectByteBufferCount(), lessThan(4L)); + assertThat(pool.getAvailableDirectByteBufferCount(), lessThan((long) buffers.size())); assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); - assertThat(pool.getDirectByteBufferCount(), lessThan(4L)); + assertThat(pool.getDirectByteBufferCount(), lessThan((long) buffers.size())); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); assertThat(pool.getDirectMemory(), greaterThan(0L)); } @@ -121,16 +128,22 @@ public void testMaxBucketSize() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, 2); - pool.acquire(1, true); // pooled - pool.acquire(1, true); // pooled - pool.acquire(1, true); // not pooled, bucket is full + RetainableByteBuffer buf1 = pool.acquire(1, true); // pooled + assertThat(buf1.capacity(), is(10)); + RetainableByteBuffer buf2 = pool.acquire(1, true); // pooled + assertThat(buf2.capacity(), is(10)); + RetainableByteBuffer buf3 = pool.acquire(1, true); // not pooled, bucket is full + assertThat(buf3.capacity(), is(1)); assertThat(pool.getDirectByteBufferCount(), is(2L)); assertThat(pool.getDirectMemory(), is(20L)); - pool.acquire(11, true); // pooled - pool.acquire(11, true); // pooled - pool.acquire(11, true); // not pooled, bucket is full + RetainableByteBuffer buf4 = pool.acquire(11, true); // pooled + assertThat(buf4.capacity(), is(20)); + RetainableByteBuffer buf5 = pool.acquire(11, true); // pooled + assertThat(buf5.capacity(), is(20)); + RetainableByteBuffer buf6 = pool.acquire(11, true); // not pooled, bucket is full + assertThat(buf6.capacity(), is(11)); assertThat(pool.getDirectByteBufferCount(), is(4L)); assertThat(pool.getDirectMemory(), is(60L)); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index f5228dffc4a9..610b11c1d340 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -232,7 +232,7 @@ void releaseRequestBuffer() { if (LOG.isDebugEnabled()) LOG.debug("releaseRequestBuffer {}", this); - if (_retainableByteBuffer.release() == 0) + if (_retainableByteBuffer.release()) _retainableByteBuffer = null; } } @@ -336,7 +336,7 @@ void parseAndFillForContent() private int fillRequestBuffer() { - if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() > 1) + if (_retainableByteBuffer != null && _retainableByteBuffer.isRetained()) throw new IllegalStateException("fill with unconsumed content on " + this); if (isRequestBufferEmpty()) @@ -385,7 +385,7 @@ private boolean parseRequestBuffer() LOG.debug("{} parsed {} {}", this, handle, _parser); // recycle buffer ? - if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() == 1) + if (_retainableByteBuffer != null && !_retainableByteBuffer.isRetained()) releaseRequestBuffer(); return handle; @@ -404,7 +404,7 @@ private boolean upgrade() _channel.recycle(); _parser.reset(); _generator.reset(); - if (_retainableByteBuffer != null && _retainableByteBuffer.getReferences() == 1) + if (_retainableByteBuffer != null && !_retainableByteBuffer.isRetained()) { releaseRequestBuffer(); } diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 5c412af037ec..d60f7a067e02 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -444,7 +444,7 @@ private void fillAndParse() } // If more references that 1(us), don't refill into buffer and risk compaction. - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); int filled = getEndPoint().fill(networkBuffer.getBuffer()); // TODO check if compact is possible. From d1d768853024ea0a90ed93e10d7d55d99f87cb23 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 09:52:20 +0200 Subject: [PATCH 47/75] set the initial retain counter to 0 + improve javadoc Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 4 ++- .../jetty/io/RetainableByteBuffer.java | 27 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 84776cd6a813..a10a67269f77 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -123,7 +123,9 @@ private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direc { ByteBuffer buffer = direct ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); BufferUtil.clear(buffer); - return new RetainableByteBuffer(buffer, releaser); + RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(buffer, releaser); + retainableByteBuffer.retain(); + return retainableByteBuffer; } private Pool bucketFor(int capacity, boolean direct) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index cd02bfdc0b8d..30d1f95cb223 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -23,14 +23,21 @@ /** * A Retainable ByteBuffer. - *

A ByteBuffer which maintains a reference count that is - * initially 1, incremented with {@link #retain()} and decremented with {@link #release()}. The buffer - * is released to the pool when the reference count is decremented to 0.

+ *

A pooled ByteBuffer which maintains a reference count that is + * incremented with {@link #retain()} and decremented with {@link #release()}. The buffer + * is released to the pool when {@link #release()} is called one more time than {@link #retain()}.

+ *

A {@code RetainableByteBuffer} can either be: + *

    + *
  • in pool; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} throws {@link IllegalStateException}
  • + *
  • out of pool but not retained; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} returns {@code true}
  • + *
  • out of pool and retained; in this case {@link #isRetained()} returns {@code true} and calling {@link #release()} returns {@code false}
  • + *
+ * Calling {@link #release()} on a out of pool and retained instance does not re-pool it while that re-pools it on a out of pool but not retained instance.

*/ public class RetainableByteBuffer implements Retainable { private final ByteBuffer buffer; - private final AtomicInteger references; + private final AtomicInteger references = new AtomicInteger(); private final Consumer releaser; private final AtomicLong lastUpdate = new AtomicLong(System.nanoTime()); @@ -38,7 +45,6 @@ public class RetainableByteBuffer implements Retainable { this.releaser = releaser; this.buffer = buffer; - this.references = new AtomicInteger(1); } public int capacity() @@ -56,6 +62,10 @@ public long getLastUpdate() return lastUpdate.getOpaque(); } + /** + * Checks if {@link #retain()} has been called at least one more time than {@link #release()}. + * @return true if this buffer is retained, false otherwise. + */ public boolean isRetained() { return references.get() > 1; @@ -66,12 +76,19 @@ public boolean isDirect() return buffer.isDirect(); } + /** + * Increments the retained counter of this buffer. + */ @Override public void retain() { references.incrementAndGet(); } + /** + * Decrements the retained counter of this buffer. + * @return true if the buffer was re-pooled, false otherwise. + */ public boolean release() { int ref = references.decrementAndGet(); From 7df2b85597c8ed13c2e890c335aa4676ff14a1cb Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 09:56:54 +0200 Subject: [PATCH 48/75] code alignment Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index acb3af61e12f..13d52fb22f33 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -423,7 +423,6 @@ public void onConnectionFailure(int error, String reason) private class NetworkBuffer implements Callback { - private final RetainableByteBuffer delegate; private NetworkBuffer() From 9dd531bf512c320b25cb0da0027d1a53088d89d6 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:12:15 +0200 Subject: [PATCH 49/75] move findOrAdapt to interface Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 38 ------------------- .../jetty/io/RetainableByteBufferPool.java | 28 +++++++++++++- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index a10a67269f77..0154c553930e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -312,42 +312,4 @@ private Pool.Entry findOldestEntry(long now, Pool releaser; - - public ByteBufferToRetainableByteBufferAdapterPool(ByteBufferPool byteBufferPool) - { - this.byteBufferPool = byteBufferPool; - this.releaser = byteBufferPool::release; - } - - @Override - public RetainableByteBuffer acquire(int size, boolean direct) - { - ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); - return new RetainableByteBuffer(byteBuffer, releaser); - } - } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index e407d410da6c..e22c9855d047 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -13,6 +13,10 @@ package org.eclipse.jetty.io; +import java.nio.ByteBuffer; + +import org.eclipse.jetty.util.component.Container; + /** *

A {@link RetainableByteBuffer} pool.

*

Acquired buffers must be released by calling {@link RetainableByteBuffer#release()} otherwise the memory they hold will @@ -21,10 +25,32 @@ public interface RetainableByteBufferPool { /** - * Acquire a memory buffer from the pool. + * Acquires a memory buffer from the pool. * @param size The size of the buffer. The returned buffer will have at least this capacity. * @param direct true if a direct memory buffer is needed, false otherwise. * @return a memory buffer. */ RetainableByteBuffer acquire(int size, boolean direct); + + /** + * Finds a {@link RetainableByteBufferPool} implementation in the given container, or wrap the given + * {@link ByteBufferPool} with an adapter. + * @param container the container to search for an existing memory pool. + * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. + * @return the {@link RetainableByteBufferPool} found or the wrapped one. + */ + static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) + { + RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + { + // Wrap the ByteBufferPool instance. + retainableByteBufferPool = (size, direct) -> + { + ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); + return new RetainableByteBuffer(byteBuffer, byteBufferPool::release); + }; + } + return retainableByteBufferPool; + } } From eaef593d19355b7c589723eb5d6dde687dd09be3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:13:32 +0200 Subject: [PATCH 50/75] remove magic constant Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index c1ab36ec399d..ff1254a8f9e0 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -227,7 +227,7 @@ public void testAcquireRelease() buf1.release(); buf2.release(); - RetainableByteBuffer buf3 = pool.acquire(16385, true); + RetainableByteBuffer buf3 = pool.acquire(16384 + 1, true); assertThat(buf3, is(notNullValue())); assertThat(buf3.capacity(), is(16384 + 1024)); buf3.release(); From 7c15fca98cccaf8d0f06872369aa51a1e025a6aa Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:20:37 +0200 Subject: [PATCH 51/75] move findOrAdapt to interface Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java | 3 +-- .../eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java | 3 +-- .../jetty/http2/client/HTTP2ClientConnectionFactory.java | 3 +-- .../http2/server/AbstractHTTP2ServerConnectionFactory.java | 3 +-- .../src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java | 3 +-- .../src/main/java/org/eclipse/jetty/server/HttpConnection.java | 3 +-- .../java/org/eclipse/jetty/server/SslConnectionFactory.java | 3 +-- .../jetty/websocket/core/client/CoreClientUpgradeRequest.java | 3 +-- .../websocket/core/server/internal/RFC6455Handshaker.java | 3 +-- .../websocket/core/server/internal/RFC8441Handshaker.java | 3 +-- 10 files changed, 10 insertions(+), 20 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index f104e75676b0..339cf86db855 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; @@ -64,7 +63,7 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } - this.retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); } @Override diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index e24a61535b8a..9b4af768f1c6 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -44,7 +44,6 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; @@ -85,7 +84,7 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr requests.addLast(0); HttpClient client = destination.getHttpClient(); - this.retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); } public HttpDestination getHttpDestination() diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index af9d2e237362..2a6be2b23be4 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -30,7 +30,6 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; @@ -69,7 +68,7 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(client, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, byteBufferPool); HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 99b3fda859b9..ff386f198fa5 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -36,7 +36,6 @@ import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.WindowRateControl; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; @@ -281,7 +280,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index 4f0ba422cb0f..c4b91b2ce564 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -34,7 +34,6 @@ import org.eclipse.jetty.io.AbstractEndPoint; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; import org.eclipse.jetty.io.RetainableByteBufferPool; @@ -192,7 +191,7 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { - this(DefaultRetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + this(RetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); } public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 610b11c1d340..bd59daaaedfa 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.io.RetainableByteBuffer; @@ -98,7 +97,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); - _retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, _bufferPool); + _retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, _bufferPool); _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index e942c918174a..3119e2e62e23 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -21,7 +21,6 @@ import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; @@ -164,7 +163,7 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index 35592d09c1a1..4d5d2e88637c 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -38,7 +38,6 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; @@ -449,7 +448,7 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index d96c56eb3736..351e7cc14eda 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -24,7 +24,6 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; @@ -98,7 +97,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index c2726e523fb5..b2bd67c9acf2 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -20,7 +20,6 @@ import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; @@ -82,7 +81,7 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); ByteBufferPool byteBufferPool = connector.getByteBufferPool(); - RetainableByteBufferPool retainableByteBufferPool = DefaultRetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } From 3bfffc4bf7ff4bf27c5b5a19f6519fcc3849d09d Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:21:27 +0200 Subject: [PATCH 52/75] add retain test Signed-off-by: Ludovic Orban --- .../DefaultRetainableByteBufferPoolTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index ff1254a8f9e0..d6faed41c870 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -96,6 +96,41 @@ void testOverMaxCapacityDoesNotPool() assertThat(pool.getDirectMemory(), is(0L)); } + @Test + public void testRetain() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + RetainableByteBuffer buf1 = pool.acquire(10, true); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + assertThat(buf1.isRetained(), is(false)); + buf1.retain(); + buf1.retain(); + assertThat(buf1.isRetained(), is(true)); + assertThat(buf1.release(), is(false)); + assertThat(buf1.isRetained(), is(true)); + assertThat(buf1.release(), is(false)); + assertThat(buf1.isRetained(), is(false)); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + assertThat(buf1.release(), is(true)); + assertThat(buf1.isRetained(), is(false)); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + } + @Test void testTooManyReleases() { From 18b92ddf67a45b0ddd6664b7101cbcd75bf1f9ec Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:23:04 +0200 Subject: [PATCH 53/75] make getRequestBuffer private Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/server/HttpConnection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index bd59daaaedfa..f0fe45e5fd4f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -236,7 +236,7 @@ void releaseRequestBuffer() } } - public ByteBuffer getRequestBuffer() + private ByteBuffer getRequestBuffer() { if (_retainableByteBuffer == null) _retainableByteBuffer = _retainableByteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); From dab16b41387bd6273b54c8c1e9c830857810ba30 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 10:26:36 +0200 Subject: [PATCH 54/75] simplify if/else Signed-off-by: Ludovic Orban --- .../eclipse/jetty/server/HttpConnection.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index f0fe45e5fd4f..a91124831b5a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -403,14 +403,17 @@ private boolean upgrade() _channel.recycle(); _parser.reset(); _generator.reset(); - if (_retainableByteBuffer != null && !_retainableByteBuffer.isRetained()) + if (_retainableByteBuffer != null) { - releaseRequestBuffer(); - } - else if (_retainableByteBuffer != null) - { - LOG.warn("{} lingering content references?!?!", this); - _retainableByteBuffer = null; // Not returned to pool! + if (!_retainableByteBuffer.isRetained()) + { + releaseRequestBuffer(); + } + else + { + LOG.warn("{} lingering content references?!?!", this); + _retainableByteBuffer = null; // Not returned to pool! + } } return true; } From b7d5e0d5ef91aab5ede916fdaed86a33566bf594 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 11:38:07 +0200 Subject: [PATCH 55/75] set the initial retain counter to 0 + improve javadoc Signed-off-by: Ludovic Orban --- .../java/org/eclipse/jetty/io/RetainableByteBufferPool.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index e22c9855d047..31282bba7426 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -48,7 +48,9 @@ static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool retainableByteBufferPool = (size, direct) -> { ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); - return new RetainableByteBuffer(byteBuffer, byteBufferPool::release); + RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(byteBuffer, byteBufferPool::release); + retainableByteBuffer.retain(); + return retainableByteBuffer; }; } return retainableByteBufferPool; From df0d3577698183578fab1c6735873c0d4f9f4994 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 11:57:14 +0200 Subject: [PATCH 56/75] force releasing the buffer on exception Signed-off-by: Ludovic Orban --- .../main/java/org/eclipse/jetty/server/HttpConnection.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index a91124831b5a..476ef4806021 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -300,7 +300,12 @@ else if (filled < 0) { if (LOG.isDebugEnabled()) LOG.debug("{} caught exception {}", this, _channel.getState(), x); - releaseRequestBuffer(); + // Force the release of the _retainableByteBuffer. + if (_retainableByteBuffer != null) + { + while (!_retainableByteBuffer.release()); + _retainableByteBuffer = null; + } getEndPoint().close(x); } finally From 491b4c6c85443801dda841a26d9b32a61009c9f3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 12:09:58 +0200 Subject: [PATCH 57/75] fix checkstyle Signed-off-by: Ludovic Orban --- .../eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index d6faed41c870..ec0dea4882f9 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -59,9 +59,9 @@ public void testMaxMemoryEviction() buffers.forEach(RetainableByteBuffer::release); assertThat(pool.getAvailableDirectByteBufferCount(), greaterThan(0L)); - assertThat(pool.getAvailableDirectByteBufferCount(), lessThan((long) buffers.size())); + assertThat(pool.getAvailableDirectByteBufferCount(), lessThan((long)buffers.size())); assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); - assertThat(pool.getDirectByteBufferCount(), lessThan((long) buffers.size())); + assertThat(pool.getDirectByteBufferCount(), lessThan((long)buffers.size())); assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); assertThat(pool.getDirectMemory(), greaterThan(0L)); } From d5aa4128a30c1d8e88b3f1abf80ae5985d57103c Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 12:43:11 +0200 Subject: [PATCH 58/75] add back retain/release safety check Signed-off-by: Ludovic Orban --- .../io/DefaultRetainableByteBufferPool.java | 5 ++-- .../jetty/io/RetainableByteBuffer.java | 19 ++++++++++++++- .../jetty/io/RetainableByteBufferPool.java | 2 +- .../DefaultRetainableByteBufferPoolTest.java | 23 +++++++++++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java index 0154c553930e..69723b80f7f6 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java @@ -23,7 +23,6 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; -import org.eclipse.jetty.util.component.Container; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,7 +113,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) else { buffer = entry.getPooled(); - buffer.retain(); + buffer.acquire(); } return buffer; } @@ -124,7 +123,7 @@ private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direc ByteBuffer buffer = direct ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); BufferUtil.clear(buffer); RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(buffer, releaser); - retainableByteBuffer.retain(); + retainableByteBuffer.acquire(); return retainableByteBuffer; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 30d1f95cb223..ce03b67431a1 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -76,13 +76,30 @@ public boolean isDirect() return buffer.isDirect(); } + /** + * Increments the retained counter of this buffer. + * It must be done right after creation and after each un-pooling. + */ + void acquire() + { + if (references.getAndIncrement() != 0) + { + references.decrementAndGet(); + throw new IllegalStateException("re-pooled while still used " + this); + } + } + /** * Increments the retained counter of this buffer. */ @Override public void retain() { - references.incrementAndGet(); + if (references.getAndIncrement() == 0) + { + references.decrementAndGet(); + throw new IllegalStateException("released " + this); + } } /** diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 31282bba7426..a4bd9d3754c2 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -49,7 +49,7 @@ static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool { ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(byteBuffer, byteBufferPool::release); - retainableByteBuffer.retain(); + retainableByteBuffer.acquire(); return retainableByteBuffer; }; } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index ec0dea4882f9..24fddd8bd589 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -246,6 +246,29 @@ public void testClearUnlinksLeakedBuffers() assertThat(pool.getAvailableDirectMemory(), is(0L)); } + @Test + public void testRetainAfterRePooledThrows() + { + DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); + RetainableByteBuffer buf1 = pool.acquire(10, true); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(buf1.release(), is(true)); + assertThrows(IllegalStateException.class, buf1::retain); + assertThrows(IllegalStateException.class, buf1::release); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + + // check that the buffer is still available + RetainableByteBuffer buf2 = pool.acquire(10, true); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(buf2 == buf1, is(true)); // make sure it's not a new instance + assertThat(buf1.release(), is(true)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + } + @Test public void testAcquireRelease() { From 920d515d836dd3b815a4a122bd476703b22369d7 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 12:58:27 +0200 Subject: [PATCH 59/75] fix tests visibility Signed-off-by: Ludovic Orban --- .../jetty/io/DefaultRetainableByteBufferPoolTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java index 24fddd8bd589..8c0b04899543 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java @@ -67,7 +67,7 @@ public void testMaxMemoryEviction() } @Test - void testBelowMinCapacityDoesNotPool() + public void testBelowMinCapacityDoesNotPool() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); @@ -82,7 +82,7 @@ void testBelowMinCapacityDoesNotPool() } @Test - void testOverMaxCapacityDoesNotPool() + public void testOverMaxCapacityDoesNotPool() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); @@ -132,7 +132,7 @@ public void testRetain() } @Test - void testTooManyReleases() + public void testTooManyReleases() { DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); From 71b75fd7fe980cee107de49b90b1f0c828648920 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 15:13:26 +0200 Subject: [PATCH 60/75] review comments Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/io/RetainableByteBuffer.java | 9 +++++---- .../org/eclipse/jetty/server/HttpConnection.java | 14 +++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index ce03b67431a1..4157dce9b16e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -22,7 +22,6 @@ import org.eclipse.jetty.util.Retainable; /** - * A Retainable ByteBuffer. *

A pooled ByteBuffer which maintains a reference count that is * incremented with {@link #retain()} and decremented with {@link #release()}. The buffer * is released to the pool when {@link #release()} is called one more time than {@link #retain()}.

@@ -32,7 +31,7 @@ *
  • out of pool but not retained; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} returns {@code true}
  • *
  • out of pool and retained; in this case {@link #isRetained()} returns {@code true} and calling {@link #release()} returns {@code false}
  • * - * Calling {@link #release()} on a out of pool and retained instance does not re-pool it while that re-pools it on a out of pool but not retained instance.

    + *

    Calling {@link #release()} on a out of pool and retained instance does not re-pool it while that re-pools it on a out of pool but not retained instance.

    */ public class RetainableByteBuffer implements Retainable { @@ -77,8 +76,10 @@ public boolean isDirect() } /** - * Increments the retained counter of this buffer. - * It must be done right after creation and after each un-pooling. + * Increments the retained counter of this buffer. It must be done internally by + * the pool right after creation and after each un-pooling. + * The reason why this method exists on top of {@link #retain()} is to be able to + * have some safety checks that must know why the ref counter is being incremented. */ void acquire() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 476ef4806021..c7755c8e8bf1 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -233,6 +233,8 @@ void releaseRequestBuffer() LOG.debug("releaseRequestBuffer {}", this); if (_retainableByteBuffer.release()) _retainableByteBuffer = null; + else + throw new IllegalStateException("unreleased buffer " + _retainableByteBuffer); } } @@ -300,11 +302,17 @@ else if (filled < 0) { if (LOG.isDebugEnabled()) LOG.debug("{} caught exception {}", this, _channel.getState(), x); - // Force the release of the _retainableByteBuffer. if (_retainableByteBuffer != null) { - while (!_retainableByteBuffer.release()); - _retainableByteBuffer = null; + _retainableByteBuffer.clear(); + try + { + releaseRequestBuffer(); + } + catch (Throwable t) + { + x.addSuppressed(t); + } } getEndPoint().close(x); } From e99698d2f29a8c130223105c6ecb9c6f032efbac Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Thu, 8 Jul 2021 19:09:10 +0200 Subject: [PATCH 61/75] review comments Signed-off-by: Ludovic Orban --- .../eclipse/jetty/server/HttpConnection.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index c7755c8e8bf1..b0fb50dc6615 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -300,21 +300,20 @@ else if (filled < 0) } catch (Throwable x) { - if (LOG.isDebugEnabled()) - LOG.debug("{} caught exception {}", this, _channel.getState(), x); - if (_retainableByteBuffer != null) + try { - _retainableByteBuffer.clear(); - try + if (LOG.isDebugEnabled()) + LOG.debug("{} caught exception {}", this, _channel.getState(), x); + if (_retainableByteBuffer != null) { + _retainableByteBuffer.clear(); releaseRequestBuffer(); } - catch (Throwable t) - { - x.addSuppressed(t); - } } - getEndPoint().close(x); + finally + { + getEndPoint().close(x); + } } finally { From 1173d0dfdce88d69aae8c9beae430f8730f7f99c Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 9 Jul 2021 11:45:03 +0200 Subject: [PATCH 62/75] rename to ArrayRetainableByteBufferPool Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/client/HttpClient.java | 4 ++-- ...ava => ArrayRetainableByteBufferPool.java} | 10 ++++---- ...=> ArrayRetainableByteBufferPoolTest.java} | 24 +++++++++---------- .../jetty/server/AbstractConnector.java | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) rename jetty-io/src/main/java/org/eclipse/jetty/io/{DefaultRetainableByteBufferPool.java => ArrayRetainableByteBufferPool.java} (95%) rename jetty-io/src/test/java/org/eclipse/jetty/io/{DefaultRetainableByteBufferPoolTest.java => ArrayRetainableByteBufferPoolTest.java} (90%) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 022cff6e38be..e3a49f061566 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -57,7 +57,7 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; @@ -202,7 +202,7 @@ protected void doStart() throws Exception if (byteBufferPool == null) setByteBufferPool(new MappedByteBufferPool(2048, maxBucketSize)); if (getBean(RetainableByteBufferPool.class) == null) - addBean(new DefaultRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); + addBean(new ArrayRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); Scheduler scheduler = getScheduler(); if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java similarity index 95% rename from jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java rename to jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 69723b80f7f6..ff358e8f0f81 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -27,9 +27,9 @@ import org.slf4j.LoggerFactory; @ManagedObject -public class DefaultRetainableByteBufferPool implements RetainableByteBufferPool +public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool { - private static final Logger LOG = LoggerFactory.getLogger(DefaultRetainableByteBufferPool.class); + private static final Logger LOG = LoggerFactory.getLogger(ArrayRetainableByteBufferPool.class); private final Pool[] _direct; private final Pool[] _indirect; @@ -40,17 +40,17 @@ public class DefaultRetainableByteBufferPool implements RetainableByteBufferPool private final AtomicLong _currentHeapMemory = new AtomicLong(); private final AtomicLong _currentDirectMemory = new AtomicLong(); - public DefaultRetainableByteBufferPool() + public ArrayRetainableByteBufferPool() { this(0, 1024, 65536, Integer.MAX_VALUE, -1L, -1L); } - public DefaultRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) + public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) { this(minCapacity, factor, maxCapacity, maxBucketSize, -1L, -1L); } - public DefaultRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { _factor = factor <= 0 ? 1024 : factor; this._maxHeapMemory = maxHeapMemory; diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java similarity index 90% rename from jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java rename to jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index 8c0b04899543..9dba1740f57d 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/DefaultRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -26,12 +26,12 @@ import static org.hamcrest.core.Is.is; import static org.junit.jupiter.api.Assertions.assertThrows; -public class DefaultRetainableByteBufferPoolTest +public class ArrayRetainableByteBufferPoolTest { @Test public void testMaxMemoryEviction() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 40, 40); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 40, 40); List buffers = new ArrayList<>(); @@ -69,7 +69,7 @@ public void testMaxMemoryEviction() @Test public void testBelowMinCapacityDoesNotPool() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); RetainableByteBuffer buf1 = pool.acquire(1, true); assertThat(buf1.capacity(), is(1)); @@ -84,7 +84,7 @@ public void testBelowMinCapacityDoesNotPool() @Test public void testOverMaxCapacityDoesNotPool() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); RetainableByteBuffer buf1 = pool.acquire(21, true); assertThat(buf1.capacity(), is(21)); @@ -99,7 +99,7 @@ public void testOverMaxCapacityDoesNotPool() @Test public void testRetain() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); RetainableByteBuffer buf1 = pool.acquire(10, true); @@ -134,7 +134,7 @@ public void testRetain() @Test public void testTooManyReleases() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); RetainableByteBuffer buf1 = pool.acquire(10, true); @@ -161,7 +161,7 @@ public void testTooManyReleases() @Test public void testMaxBucketSize() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, 2); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, 2); RetainableByteBuffer buf1 = pool.acquire(1, true); // pooled assertThat(buf1.capacity(), is(10)); @@ -187,7 +187,7 @@ public void testMaxBucketSize() @Test public void testBufferReleaseRepools() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(0, 10, 20, 1); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, 1); List all = new ArrayList<>(); @@ -212,7 +212,7 @@ public void testBufferReleaseRepools() @Test public void testFactorAndCapacity() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); pool.acquire(1, true); // not pooled, < minCapacity pool.acquire(10, true); // pooled @@ -228,7 +228,7 @@ public void testFactorAndCapacity() @Test public void testClearUnlinksLeakedBuffers() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); pool.acquire(10, true); pool.acquire(10, true); @@ -249,7 +249,7 @@ public void testClearUnlinksLeakedBuffers() @Test public void testRetainAfterRePooledThrows() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); RetainableByteBuffer buf1 = pool.acquire(10, true); assertThat(pool.getDirectByteBufferCount(), is(1L)); assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); @@ -272,7 +272,7 @@ public void testRetainAfterRePooledThrows() @Test public void testAcquireRelease() { - DefaultRetainableByteBufferPool pool = new DefaultRetainableByteBufferPool(); + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); for (int i = 0; i < 3; i++) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 76ec5d434258..e4bf1e8e4f1b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -33,7 +33,7 @@ import org.eclipse.jetty.io.ArrayByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.DefaultRetainableByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; @@ -191,7 +191,7 @@ public AbstractConnector( _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); - addBean(retainableByteBufferPool == null ? new DefaultRetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); + addBean(retainableByteBufferPool == null ? new ArrayRetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); addEventListener(new Container.Listener() { From 8578783f8344c27d4d144b1605ea9c10aff532d6 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 9 Jul 2021 11:57:53 +0200 Subject: [PATCH 63/75] do bound checking in an atomic function to avoid exposing illegal values Signed-off-by: Ludovic Orban --- .../jetty/io/RetainableByteBuffer.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 4157dce9b16e..b760d00b8771 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -83,11 +83,8 @@ public boolean isDirect() */ void acquire() { - if (references.getAndIncrement() != 0) - { - references.decrementAndGet(); + if (references.getAndUpdate(c -> c == 0 ? 1 : c) != 0) throw new IllegalStateException("re-pooled while still used " + this); - } } /** @@ -96,11 +93,8 @@ void acquire() @Override public void retain() { - if (references.getAndIncrement() == 0) - { - references.decrementAndGet(); + if (references.getAndUpdate(c -> c == 0 ? 0 : c + 1) == 0) throw new IllegalStateException("released " + this); - } } /** @@ -109,12 +103,12 @@ public void retain() */ public boolean release() { - int ref = references.decrementAndGet(); - if (ref < 0) + int ref = references.updateAndGet(c -> { - references.incrementAndGet(); - throw new IllegalStateException("already released " + this); - } + if (c == 0) + throw new IllegalStateException("already released " + this); + return c - 1; + }); if (ref == 0) { lastUpdate.setOpaque(System.nanoTime()); From 410d40268dc0b7465569ff259b44dcff6de39b26 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 9 Jul 2021 12:36:51 +0200 Subject: [PATCH 64/75] fix checkstyle Signed-off-by: Ludovic Orban --- .../src/main/java/org/eclipse/jetty/client/HttpClient.java | 2 +- .../main/java/org/eclipse/jetty/server/AbstractConnector.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index e3a49f061566..abe97883d9ba 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -54,10 +54,10 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index e4bf1e8e4f1b..0a020a44c010 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -32,8 +32,8 @@ import java.util.stream.Collectors; import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; From f2b545c45a4f240a1849a662a7080fafdaadae5d Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 12 Jul 2021 12:53:46 +1000 Subject: [PATCH 65/75] Improve #6322 log buckets in RetainableByteBufferPool Add a Log2 bucket size imple for ArrayRetainableByteBufferPool Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 93 +++++++++++++++---- .../io/ArrayRetainableByteBufferPoolTest.java | 28 ++++++ .../java/org/eclipse/jetty/util/TypeUtil.java | 38 ++++++++ .../org/eclipse/jetty/util/TypeUtilTest.java | 55 ++++++++++- 4 files changed, 195 insertions(+), 19 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index ff358e8f0f81..873e85134f5e 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -17,9 +17,11 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; +import java.util.function.Function; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; @@ -31,14 +33,17 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool { private static final Logger LOG = LoggerFactory.getLogger(ArrayRetainableByteBufferPool.class); - private final Pool[] _direct; - private final Pool[] _indirect; + private final Bucket[] _direct; + private final Bucket[] _indirect; private final int _factor; private final int _minCapacity; + private final int _maxCapacity; private final long _maxHeapMemory; private final long _maxDirectMemory; private final AtomicLong _currentHeapMemory = new AtomicLong(); private final AtomicLong _currentDirectMemory = new AtomicLong(); + private final Function _bucketIndexFor; + private final Function _bucketCapacity; public ArrayRetainableByteBufferPool() { @@ -51,6 +56,14 @@ public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacit } public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + { + this(minCapacity, factor, maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, + c -> (c - 1) / factor, + i -> (i + 1) * factor); + } + + protected ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory, + Function bucketIndexFor, Function bucketCapacity) { _factor = factor <= 0 ? 1024 : factor; this._maxHeapMemory = maxHeapMemory; @@ -58,31 +71,44 @@ public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacit if (minCapacity <= 0) minCapacity = 0; _minCapacity = minCapacity; + _maxCapacity = maxCapacity; if (maxCapacity <= 0) maxCapacity = 64 * 1024; if ((maxCapacity % _factor) != 0 || _factor >= maxCapacity) throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); - int length = maxCapacity / _factor; + int length = bucketIndexFor.apply(maxCapacity) + 1; @SuppressWarnings("unchecked") - Pool[] directArray = new Pool[length]; + Bucket[] directArray = new Bucket[length]; @SuppressWarnings("unchecked") - Pool[] indirectArray = new Pool[length]; + Bucket[] indirectArray = new Bucket[length]; for (int i = 0; i < directArray.length; i++) { - directArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); - indirectArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); + int capacity = bucketCapacity.apply(i); + directArray[i] = new Bucket(capacity, maxBucketSize); + indirectArray[i] = new Bucket(capacity, maxBucketSize); } _direct = directArray; _indirect = indirectArray; + _bucketIndexFor = bucketIndexFor; + _bucketCapacity = bucketCapacity; + } + + public int getMinCapacity() + { + return _minCapacity; + } + + public int getMaxCapacity() + { + return _maxCapacity; } @Override public RetainableByteBuffer acquire(int size, boolean direct) { - int capacity = (bucketIndexFor(size) + 1) * _factor; - Pool bucket = bucketFor(size, direct); + Bucket bucket = bucketFor(size, direct); if (bucket == null) return newRetainableByteBuffer(size, direct, byteBuffer -> {}); Pool.Entry entry = bucket.acquire(); @@ -93,7 +119,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) Pool.Entry reservedEntry = bucket.reserve(); if (reservedEntry != null) { - buffer = newRetainableByteBuffer(capacity, direct, byteBuffer -> + buffer = newRetainableByteBuffer(bucket.capacity, direct, byteBuffer -> { BufferUtil.clear(byteBuffer); reservedEntry.release(); @@ -127,22 +153,17 @@ private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direc return retainableByteBuffer; } - private Pool bucketFor(int capacity, boolean direct) + private Bucket bucketFor(int capacity, boolean direct) { if (capacity < _minCapacity) return null; - int idx = bucketIndexFor(capacity); - Pool[] buckets = direct ? _direct : _indirect; + int idx = _bucketIndexFor.apply(capacity); + Bucket[] buckets = direct ? _direct : _indirect; if (idx >= buckets.length) return null; return buckets[idx]; } - private int bucketIndexFor(int capacity) - { - return (capacity - 1) / _factor; - } - @ManagedAttribute("The number of pooled direct ByteBuffers") public long getDirectByteBufferCount() { @@ -311,4 +332,40 @@ private Pool.Entry findOldestEntry(long now, Pool + { + private final int capacity; + + Bucket(int capacity, int size) + { + super(Pool.StrategyType.THREAD_ID, size, true); + this.capacity = capacity; + } + } + + public static class LogBuckets extends ArrayRetainableByteBufferPool + { + public LogBuckets() + { + this(0, 65536, Integer.MAX_VALUE); + } + + public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize) + { + this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); + } + + public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + { + super(minCapacity, + TypeUtil.nextPowerOf2(maxCapacity) / 2, + TypeUtil.nextPowerOf2(maxCapacity), + maxBucketSize, + maxHeapMemory, + maxDirectMemory, + c -> TypeUtil.log2NextPowerOf2(c), + i -> 1 << i); + } + } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index 9dba1740f57d..fc6858c5eef8 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.List; +import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; @@ -313,4 +314,31 @@ public void testAcquireRelease() assertThat(pool.getDirectMemory(), is(0L)); assertThat(pool.getHeapMemory(), is(0L)); } + + @Test + public void testLogBuckets() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool.LogBuckets(); + assertThat(pool.acquire(1, false).capacity(), is(1)); + assertThat(pool.acquire(2, false).capacity(), is(2)); + assertThat(pool.acquire(3, false).capacity(), is(4)); + assertThat(pool.acquire(4, false).capacity(), is(4)); + + int capacity = 4; + while (true) + { + RetainableByteBuffer b = pool.acquire(capacity - 1, false); + assertThat(b.capacity(), Matchers.is(capacity)); + b = pool.acquire(capacity, false); + assertThat(b.capacity(), Matchers.is(capacity)); + + if (capacity >= pool.getMaxCapacity()) + break; + + b = pool.acquire(capacity + 1, false); + assertThat(b.capacity(), Matchers.is(capacity * 2)); + + capacity = capacity * 2; + } + } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index 5d937f809d1e..2302ad4ee925 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -800,4 +800,42 @@ public static Stream> serviceProviderStream(Servic { return StreamSupport.stream(new ServiceLoaderSpliterator<>(serviceLoader), false); } + + /** + * Round up to the next power of 2. + * @param v An integer > 0 and <= half of {@link Integer#MAX_VALUE} + * @return The next power of two that is equal too or larger than the passed integer. + */ + public static int nextPowerOf2(int v) + { + if (v < 0 || v > (Integer.MAX_VALUE / 2)) + throw new IllegalArgumentException(Integer.toString(v)); + + // This algorithm is from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + // and gives good performance on most architectures. + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; + } + + private static final int[] MultiplyDeBruijnBitPosition2 = + { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + + /** + * The log2 of the value rounded up to the next power of 2. + * @param v An integer > 0 and <= half of {@link Integer#MAX_VALUE} + * @return The log2 of next power of two that is equal too or larger than the passed integer. + */ + public static int log2NextPowerOf2(int v) + { + // This algorithm is from https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn + return MultiplyDeBruijnBitPosition2[(int)((0xFFFFFFFFL & (nextPowerOf2(v) * 0x077CB531L)) >> 27)]; + } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java index 1d6851747a7f..7bf6222fd529 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java @@ -19,12 +19,12 @@ import org.eclipse.jetty.util.resource.Resource; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -188,4 +188,57 @@ public void testGetLocationJavaLangThreadDeathJPMS() String expectedJavaBase = "/java.base"; assertThat(TypeUtil.getLocationOfClass(java.lang.ThreadDeath.class).toASCIIString(), containsString(expectedJavaBase)); } + + @Test + public void testNextPowerOf2() + { + assertThat(TypeUtil.nextPowerOf2(0), Matchers.is(0)); + assertThat(TypeUtil.nextPowerOf2(1), Matchers.is(1)); + assertThat(TypeUtil.nextPowerOf2(2), Matchers.is(2)); + assertThat(TypeUtil.nextPowerOf2(3), Matchers.is(4)); + assertThat(TypeUtil.nextPowerOf2(4), Matchers.is(4)); + + int value = 4; + while (value < Integer.MAX_VALUE / 2) + { + assertThat(TypeUtil.nextPowerOf2(value - 1), Matchers.is(value)); + assertThat(TypeUtil.nextPowerOf2(value), Matchers.is(value)); + assertThat(TypeUtil.nextPowerOf2(value + 1), Matchers.is(value * 2)); + value = value * 2; + } + + assertThat(TypeUtil.nextPowerOf2(Integer.MAX_VALUE / 2), Matchers.is(0x40000000)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2((Integer.MAX_VALUE / 2) + 1)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(Integer.MAX_VALUE)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(-1)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(Integer.MIN_VALUE)); + } + + @Test + public void testLogNextPowerOf2() + { + assertThat(TypeUtil.log2NextPowerOf2(0), Matchers.is(0)); + assertThat(TypeUtil.log2NextPowerOf2(1), Matchers.is(0)); + assertThat(TypeUtil.log2NextPowerOf2(2), Matchers.is(1)); + assertThat(TypeUtil.log2NextPowerOf2(3), Matchers.is(2)); + assertThat(TypeUtil.log2NextPowerOf2(4), Matchers.is(2)); + + int value = 4; + int power = 2; + while (value < Integer.MAX_VALUE / 2) + { + System.err.printf("v=%d p=%d%n", value, power); + assertThat(TypeUtil.log2NextPowerOf2(value - 1), Matchers.is(power)); + assertThat(TypeUtil.log2NextPowerOf2(value), Matchers.is(power)); + assertThat(TypeUtil.log2NextPowerOf2(value + 1), Matchers.is(power + 1)); + value = value * 2; + power = power + 1; + } + + assertThat(TypeUtil.log2NextPowerOf2(Integer.MAX_VALUE / 2), Matchers.is(30)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2((Integer.MAX_VALUE / 2) + 1)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(Integer.MAX_VALUE)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(-1)); + assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(Integer.MIN_VALUE)); + } } From 8feecc05045f41a31eb09d187b7c45a9041b648e Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 12 Jul 2021 13:53:52 +1000 Subject: [PATCH 66/75] Improve #6322 log buckets in RetainableByteBufferPool Add a Log2 bucket size impl for ArrayRetainableByteBufferPool Signed-off-by: Greg Wilkins --- .../jetty/io/ArrayRetainableByteBufferPool.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 873e85134f5e..f740c9486d82 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -119,7 +119,7 @@ public RetainableByteBuffer acquire(int size, boolean direct) Pool.Entry reservedEntry = bucket.reserve(); if (reservedEntry != null) { - buffer = newRetainableByteBuffer(bucket.capacity, direct, byteBuffer -> + buffer = newRetainableByteBuffer(bucket._capacity, direct, byteBuffer -> { BufferUtil.clear(byteBuffer); reservedEntry.release(); @@ -335,15 +335,20 @@ private Pool.Entry findOldestEntry(long now, Pool { - private final int capacity; + private final int _capacity; Bucket(int capacity, int size) { super(Pool.StrategyType.THREAD_ID, size, true); - this.capacity = capacity; + _capacity = capacity; } } + /** + * A variant of the {@link ArrayRetainableByteBufferPool} that + * uses buckets of buffers that increase in size by a power of + * 2 (eg 1k, 2k, 4k, 8k, etc.). + */ public static class LogBuckets extends ArrayRetainableByteBufferPool { public LogBuckets() From 3b1700c9408f13e2be0fb1104ad28c754c863064 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 12 Jul 2021 17:11:02 +1000 Subject: [PATCH 67/75] Cleanups Use Bucket everywhere Use -1 to indicate defaults Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 78 ++++++++++--------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index f740c9486d82..630d9ebe23b8 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -35,7 +35,6 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool private final Bucket[] _direct; private final Bucket[] _indirect; - private final int _factor; private final int _minCapacity; private final int _maxCapacity; private final long _maxHeapMemory; @@ -43,11 +42,10 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool private final AtomicLong _currentHeapMemory = new AtomicLong(); private final AtomicLong _currentDirectMemory = new AtomicLong(); private final Function _bucketIndexFor; - private final Function _bucketCapacity; public ArrayRetainableByteBufferPool() { - this(0, 1024, 65536, Integer.MAX_VALUE, -1L, -1L); + this(0, -1, -1, Integer.MAX_VALUE, -1L, -1L); } public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) @@ -57,49 +55,54 @@ public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacit public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { - this(minCapacity, factor, maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, - c -> (c - 1) / factor, - i -> (i + 1) * factor); + this(minCapacity, factor, maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, null, null); } protected ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory, Function bucketIndexFor, Function bucketCapacity) { - _factor = factor <= 0 ? 1024 : factor; - this._maxHeapMemory = maxHeapMemory; - this._maxDirectMemory = maxDirectMemory; if (minCapacity <= 0) minCapacity = 0; - _minCapacity = minCapacity; - _maxCapacity = maxCapacity; if (maxCapacity <= 0) maxCapacity = 64 * 1024; - if ((maxCapacity % _factor) != 0 || _factor >= maxCapacity) + + final int f = factor <= 0 ? 1024 : factor; + if ((maxCapacity % f) != 0 || f >= maxCapacity) throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); - int length = bucketIndexFor.apply(maxCapacity) + 1; + if (bucketIndexFor == null) + bucketIndexFor = c -> (c - 1) / f; + if (bucketCapacity == null) + bucketCapacity = i -> (i + 1) * f; + int length = bucketIndexFor.apply(maxCapacity) + 1; @SuppressWarnings("unchecked") Bucket[] directArray = new Bucket[length]; @SuppressWarnings("unchecked") Bucket[] indirectArray = new Bucket[length]; for (int i = 0; i < directArray.length; i++) { - int capacity = bucketCapacity.apply(i); + int capacity = Math.min(bucketCapacity.apply(i), maxCapacity); directArray[i] = new Bucket(capacity, maxBucketSize); indirectArray[i] = new Bucket(capacity, maxBucketSize); } + + _minCapacity = minCapacity; + _maxCapacity = maxCapacity; _direct = directArray; _indirect = indirectArray; + _maxHeapMemory = maxHeapMemory; + _maxDirectMemory = maxDirectMemory; _bucketIndexFor = bucketIndexFor; - _bucketCapacity = bucketCapacity; } + @ManagedAttribute("The minimum pooled buffer capacity") public int getMinCapacity() { return _minCapacity; } + @ManagedAttribute("The maximum pooled buffer capacity") public int getMaxCapacity() { return _maxCapacity; @@ -111,12 +114,12 @@ public RetainableByteBuffer acquire(int size, boolean direct) Bucket bucket = bucketFor(size, direct); if (bucket == null) return newRetainableByteBuffer(size, direct, byteBuffer -> {}); - Pool.Entry entry = bucket.acquire(); + Bucket.Entry entry = bucket.acquire(); RetainableByteBuffer buffer; if (entry == null) { - Pool.Entry reservedEntry = bucket.reserve(); + Bucket.Entry reservedEntry = bucket.reserve(); if (reservedEntry != null) { buffer = newRetainableByteBuffer(bucket._capacity, direct, byteBuffer -> @@ -178,8 +181,8 @@ public long getHeapByteBufferCount() private long getByteBufferCount(boolean direct) { - Pool[] buckets = direct ? _direct : _indirect; - return Arrays.stream(buckets).mapToLong(Pool::size).sum(); + Bucket[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(Bucket::size).sum(); } @ManagedAttribute("The number of pooled direct ByteBuffers that are available") @@ -196,8 +199,8 @@ public long getAvailableHeapByteBufferCount() private long getAvailableByteBufferCount(boolean direct) { - Pool[] buckets = direct ? _direct : _indirect; - return Arrays.stream(buckets).mapToLong(pool -> pool.values().stream().filter(Pool.Entry::isIdle).count()).sum(); + Bucket[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(bucket -> bucket.values().stream().filter(Pool.Entry::isIdle).count()).sum(); } @ManagedAttribute("The bytes retained by direct ByteBuffers") @@ -234,12 +237,11 @@ public long getAvailableHeapMemory() private long getAvailableMemory(boolean direct) { - Pool[] buckets = direct ? _direct : _indirect; + Bucket[] buckets = direct ? _direct : _indirect; long total = 0L; - for (int i = 0; i < buckets.length; i++) + for (Bucket bucket : buckets) { - Pool bucket = buckets[i]; - long capacity = (i + 1L) * _factor; + int capacity = bucket._capacity; total += bucket.values().stream().filter(Pool.Entry::isIdle).count() * capacity; } return total; @@ -252,11 +254,11 @@ public void clear() clearArray(_indirect, _currentHeapMemory); } - private void clearArray(Pool[] poolArray, AtomicLong memoryCounter) + private void clearArray(Bucket[] poolArray, AtomicLong memoryCounter) { - for (Pool retainableByteBufferPool : poolArray) + for (Bucket pool : poolArray) { - for (Pool.Entry entry : retainableByteBufferPool.values()) + for (Bucket.Entry entry : pool.values()) { entry.remove(); memoryCounter.addAndGet(-entry.getPooled().capacity()); @@ -287,13 +289,13 @@ private void evict(boolean direct, long excess) long now = System.nanoTime(); long totalClearedCapacity = 0L; - Pool[] buckets = direct ? _direct : _indirect; + Bucket[] buckets = direct ? _direct : _indirect; while (totalClearedCapacity < excess) { - for (Pool bucket : buckets) + for (Bucket bucket : buckets) { - Pool.Entry oldestEntry = findOldestEntry(now, bucket); + Bucket.Entry oldestEntry = findOldestEntry(now, bucket); if (oldestEntry == null) continue; @@ -314,10 +316,10 @@ private void evict(boolean direct, long excess) LOG.debug("eviction done, cleared {} bytes from {} pools", totalClearedCapacity, (direct ? "direct" : "heap")); } - private Pool.Entry findOldestEntry(long now, Pool bucket) + private Bucket.Entry findOldestEntry(long now, Bucket bucket) { - Pool.Entry oldestEntry = null; - for (Pool.Entry entry : bucket.values()) + Bucket.Entry oldestEntry = null; + for (Bucket.Entry entry : bucket.values()) { if (oldestEntry != null) { @@ -353,7 +355,7 @@ public static class LogBuckets extends ArrayRetainableByteBufferPool { public LogBuckets() { - this(0, 65536, Integer.MAX_VALUE); + this(0, -1, Integer.MAX_VALUE); } public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize) @@ -364,12 +366,12 @@ public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize) public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { super(minCapacity, - TypeUtil.nextPowerOf2(maxCapacity) / 2, - TypeUtil.nextPowerOf2(maxCapacity), + -1, + maxCapacity, maxBucketSize, maxHeapMemory, maxDirectMemory, - c -> TypeUtil.log2NextPowerOf2(c), + TypeUtil::log2NextPowerOf2, i -> 1 << i); } } From cfc0a9b893e2eed1e918095dad682e4e837a29d3 Mon Sep 17 00:00:00 2001 From: Ludovic Orban Date: Fri, 28 May 2021 11:51:57 +0200 Subject: [PATCH 68/75] #6322 Use RetainableByteBuffer and write a new pool for it Signed-off-by: Ludovic Orban --- .../org/eclipse/jetty/client/HttpClient.java | 12 +- .../client/http/HttpReceiverOverHTTP.java | 10 +- .../client/http/HttpConnectionOverFCGI.java | 11 +- .../client/HTTP2ClientConnectionFactory.java | 9 +- .../eclipse/jetty/http2/HTTP2Connection.java | 45 ++- .../AbstractHTTP2ServerConnectionFactory.java | 5 +- .../http2/server/HTTP2ServerConnection.java | 6 +- .../io/ArrayRetainableByteBufferPool.java | 314 +++++++++++++++++ .../jetty/io/RetainableByteBuffer.java | 100 ++++-- .../jetty/io/RetainableByteBufferPool.java | 58 ++++ .../eclipse/jetty/io/ssl/SslConnection.java | 38 ++- .../io/ArrayRetainableByteBufferPoolTest.java | 316 ++++++++++++++++++ .../jetty/server/AbstractConnector.java | 4 + .../eclipse/jetty/server/HttpConnection.java | 109 +++--- .../jetty/server/SslConnectionFactory.java | 6 +- .../core/client/CoreClientUpgradeRequest.java | 4 +- .../core/internal/WebSocketConnection.java | 13 +- .../server/internal/AbstractHandshaker.java | 5 +- .../server/internal/RFC6455Handshaker.java | 6 +- .../server/internal/RFC8441Handshaker.java | 6 +- 20 files changed, 944 insertions(+), 133 deletions(-) create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java create mode 100644 jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java index 84f19e47f9a6..2e89a292a4d4 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java @@ -54,10 +54,12 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.io.MappedByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Jetty; @@ -193,12 +195,14 @@ protected void doStart() throws Exception threadPool.setName(name); setExecutor(threadPool); } + int maxBucketSize = executor instanceof ThreadPool.SizedThreadPool + ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 + : ProcessorUtils.availableProcessors() * 2; ByteBufferPool byteBufferPool = getByteBufferPool(); if (byteBufferPool == null) - setByteBufferPool(new MappedByteBufferPool(2048, - executor instanceof ThreadPool.SizedThreadPool - ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads() / 2 - : ProcessorUtils.availableProcessors() * 2)); + setByteBufferPool(new MappedByteBufferPool(2048, maxBucketSize)); + if (getBean(RetainableByteBufferPool.class) == null) + addBean(new ArrayRetainableByteBufferPool(0, 2048, 65536, maxBucketSize)); Scheduler scheduler = getScheduler(); if (scheduler == null) setScheduler(new ScheduledExecutorScheduler(name + "-scheduler", false)); diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java index 04b4843775d5..339cf86db855 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java @@ -29,9 +29,9 @@ import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.slf4j.Logger; @@ -43,6 +43,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res private final LongAdder inMessages = new LongAdder(); private final HttpParser parser; + private final RetainableByteBufferPool retainableByteBufferPool; private RetainableByteBuffer networkBuffer; private boolean shutdown; private boolean complete; @@ -61,6 +62,8 @@ public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) parser.setHeaderCacheSize(httpTransport.getHeaderCacheSize()); parser.setHeaderCacheCaseSensitive(httpTransport.isHeaderCacheCaseSensitive()); } + + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(httpClient, httpClient.getByteBufferPool()); } @Override @@ -111,9 +114,8 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer() { HttpClient client = getHttpDestination().getHttpClient(); - ByteBufferPool bufferPool = client.getByteBufferPool(); boolean direct = client.isUseInputDirectByteBuffers(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), direct); + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), direct); } private void releaseNetworkBuffer() @@ -166,7 +168,7 @@ private void process() return; } - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java index a78f14ddeb4a..cf49a458e299 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpConnectionOverFCGI.java @@ -44,9 +44,9 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Attachable; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -71,6 +71,7 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements IConne private final ClientParser parser; private RetainableByteBuffer networkBuffer; private Object attachment; + private final RetainableByteBufferPool retainableByteBufferPool; public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Promise promise) { @@ -81,6 +82,9 @@ public HttpConnectionOverFCGI(EndPoint endPoint, HttpDestination destination, Pr this.delegate = new Delegate(destination); this.parser = new ClientParser(new ResponseListener()); requests.addLast(0); + + HttpClient client = destination.getHttpClient(); + this.retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, client.getByteBufferPool()); } public HttpDestination getHttpDestination() @@ -135,8 +139,7 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer() { HttpClient client = destination.getHttpClient(); - ByteBufferPool bufferPool = client.getByteBufferPool(); - return new RetainableByteBuffer(bufferPool, client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); + return retainableByteBufferPool.acquire(client.getResponseBufferSize(), client.isUseInputDirectByteBuffers()); } private void releaseNetworkBuffer() @@ -161,7 +164,7 @@ void process() if (parse(networkBuffer.getBuffer())) return; - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); // The networkBuffer may have been reacquired. diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java index 228267b308f6..2a6be2b23be4 100644 --- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java +++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java @@ -31,6 +31,7 @@ import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.LifeCycle; @@ -67,7 +68,9 @@ public Connection newConnection(EndPoint endPoint, Map context) parser.setMaxFrameLength(client.getMaxFrameLength()); parser.setMaxSettingsKeys(client.getMaxSettingsKeys()); - HTTP2ClientConnection connection = new HTTP2ClientConnection(client, byteBufferPool, executor, endPoint, + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(client, byteBufferPool); + + HTTP2ClientConnection connection = new HTTP2ClientConnection(client, retainableByteBufferPool, executor, endPoint, parser, session, client.getInputBufferSize(), promise, listener); connection.setUseInputDirectByteBuffers(client.isUseInputDirectByteBuffers()); connection.setUseOutputDirectByteBuffers(client.isUseOutputDirectByteBuffers()); @@ -81,9 +84,9 @@ private static class HTTP2ClientConnection extends HTTP2Connection implements Ca private final Promise promise; private final Session.Listener listener; - private HTTP2ClientConnection(HTTP2Client client, ByteBufferPool byteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) + private HTTP2ClientConnection(HTTP2Client client, RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endpoint, Parser parser, ISession session, int bufferSize, Promise promise, Session.Listener listener) { - super(byteBufferPool, executor, endpoint, parser, session, bufferSize); + super(retainableByteBufferPool, executor, endpoint, parser, session, bufferSize); this.client = client; this.promise = promise; this.listener = listener; diff --git a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java index 02dabdb702a9..2632cfab6c07 100644 --- a/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java +++ b/jetty-http2/http2-common/src/main/java/org/eclipse/jetty/http2/HTTP2Connection.java @@ -23,10 +23,10 @@ import org.eclipse.jetty.http2.frames.DataFrame; import org.eclipse.jetty.http2.parser.Parser; import org.eclipse.jetty.io.AbstractConnection; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -45,7 +45,7 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private final Queue tasks = new ArrayDeque<>(); private final HTTP2Producer producer = new HTTP2Producer(); private final AtomicLong bytesIn = new AtomicLong(); - private final ByteBufferPool byteBufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Parser parser; private final ISession session; private final int bufferSize; @@ -53,10 +53,10 @@ public class HTTP2Connection extends AbstractConnection implements WriteFlusher. private boolean useInputDirectByteBuffers; private boolean useOutputDirectByteBuffers; - public HTTP2Connection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) + protected HTTP2Connection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, Parser parser, ISession session, int bufferSize) { super(endPoint, executor); - this.byteBufferPool = byteBufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.parser = parser; this.session = session; this.bufferSize = bufferSize; @@ -287,7 +287,7 @@ public Runnable produce() return task; // If more references than 1 (ie not just us), don't refill into buffer and risk compaction. - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); } @@ -415,16 +415,43 @@ public void onConnectionFailure(int error, String reason) } } - private class NetworkBuffer extends RetainableByteBuffer implements Callback + private class NetworkBuffer implements Callback { + private final RetainableByteBuffer delegate; + private NetworkBuffer() { - super(byteBufferPool, bufferSize, isUseInputDirectByteBuffers()); + delegate = retainableByteBufferPool.acquire(bufferSize, isUseInputDirectByteBuffers()); + } + + public ByteBuffer getBuffer() + { + return delegate.getBuffer(); + } + + public boolean isRetained() + { + return delegate.isRetained(); + } + + public boolean hasRemaining() + { + return delegate.hasRemaining(); + } + + public boolean release() + { + return delegate.release(); + } + + public void retain() + { + delegate.retain(); } private void put(ByteBuffer source) { - BufferUtil.append(getBuffer(), source); + BufferUtil.append(delegate.getBuffer(), source); } @Override @@ -441,7 +468,7 @@ public void failed(Throwable failure) private void completed(Throwable failure) { - if (release() == 0) + if (delegate.release()) { if (LOG.isDebugEnabled()) LOG.debug("Released retained {}", this, failure); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 76b91c92776b..ff386f198fa5 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.http2.parser.WindowRateControl; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; @@ -279,7 +280,9 @@ public Connection newConnection(Connector connector, EndPoint endPoint) parser.setMaxFrameLength(getMaxFrameLength()); parser.setMaxSettingsKeys(getMaxSettingsKeys()); - HTTP2Connection connection = new HTTP2ServerConnection(connector.getByteBufferPool(), connector.getExecutor(), + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, connector.getByteBufferPool()); + + HTTP2Connection connection = new HTTP2ServerConnection(retainableByteBufferPool, connector.getExecutor(), endPoint, httpConfiguration, parser, session, getInputBufferSize(), listener); connection.setUseInputDirectByteBuffers(isUseInputDirectByteBuffers()); connection.setUseOutputDirectByteBuffers(isUseOutputDirectByteBuffers()); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java index f3037941374c..09bd604979a3 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnection.java @@ -45,8 +45,8 @@ import org.eclipse.jetty.http2.frames.SettingsFrame; import org.eclipse.jetty.http2.parser.ServerParser; import org.eclipse.jetty.http2.parser.SettingsBodyParser; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.util.BufferUtil; @@ -87,9 +87,9 @@ public static boolean isSupportedProtocol(String protocol) private final HttpConfiguration httpConfig; private boolean recycleHttpChannels = true; - public HTTP2ServerConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) + public HTTP2ServerConnection(RetainableByteBufferPool retainableByteBufferPool, Executor executor, EndPoint endPoint, HttpConfiguration httpConfig, ServerParser parser, ISession session, int inputBufferSize, ServerSessionListener listener) { - super(byteBufferPool, executor, endPoint, parser, session, inputBufferSize); + super(retainableByteBufferPool, executor, endPoint, parser, session, inputBufferSize); this.listener = listener; this.httpConfig = httpConfig; } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java new file mode 100644 index 000000000000..ff358e8f0f81 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -0,0 +1,314 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Pool; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ManagedObject +public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool +{ + private static final Logger LOG = LoggerFactory.getLogger(ArrayRetainableByteBufferPool.class); + + private final Pool[] _direct; + private final Pool[] _indirect; + private final int _factor; + private final int _minCapacity; + private final long _maxHeapMemory; + private final long _maxDirectMemory; + private final AtomicLong _currentHeapMemory = new AtomicLong(); + private final AtomicLong _currentDirectMemory = new AtomicLong(); + + public ArrayRetainableByteBufferPool() + { + this(0, 1024, 65536, Integer.MAX_VALUE, -1L, -1L); + } + + public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize) + { + this(minCapacity, factor, maxCapacity, maxBucketSize, -1L, -1L); + } + + public ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + { + _factor = factor <= 0 ? 1024 : factor; + this._maxHeapMemory = maxHeapMemory; + this._maxDirectMemory = maxDirectMemory; + if (minCapacity <= 0) + minCapacity = 0; + _minCapacity = minCapacity; + if (maxCapacity <= 0) + maxCapacity = 64 * 1024; + if ((maxCapacity % _factor) != 0 || _factor >= maxCapacity) + throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); + + int length = maxCapacity / _factor; + + @SuppressWarnings("unchecked") + Pool[] directArray = new Pool[length]; + @SuppressWarnings("unchecked") + Pool[] indirectArray = new Pool[length]; + for (int i = 0; i < directArray.length; i++) + { + directArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); + indirectArray[i] = new Pool<>(Pool.StrategyType.THREAD_ID, maxBucketSize, true); + } + _direct = directArray; + _indirect = indirectArray; + } + + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + int capacity = (bucketIndexFor(size) + 1) * _factor; + Pool bucket = bucketFor(size, direct); + if (bucket == null) + return newRetainableByteBuffer(size, direct, byteBuffer -> {}); + Pool.Entry entry = bucket.acquire(); + + RetainableByteBuffer buffer; + if (entry == null) + { + Pool.Entry reservedEntry = bucket.reserve(); + if (reservedEntry != null) + { + buffer = newRetainableByteBuffer(capacity, direct, byteBuffer -> + { + BufferUtil.clear(byteBuffer); + reservedEntry.release(); + }); + reservedEntry.enable(buffer, true); + if (direct) + _currentDirectMemory.addAndGet(buffer.capacity()); + else + _currentHeapMemory.addAndGet(buffer.capacity()); + releaseExcessMemory(direct); + } + else + { + buffer = newRetainableByteBuffer(size, direct, byteBuffer -> {}); + } + } + else + { + buffer = entry.getPooled(); + buffer.acquire(); + } + return buffer; + } + + private RetainableByteBuffer newRetainableByteBuffer(int capacity, boolean direct, Consumer releaser) + { + ByteBuffer buffer = direct ? ByteBuffer.allocateDirect(capacity) : ByteBuffer.allocate(capacity); + BufferUtil.clear(buffer); + RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(buffer, releaser); + retainableByteBuffer.acquire(); + return retainableByteBuffer; + } + + private Pool bucketFor(int capacity, boolean direct) + { + if (capacity < _minCapacity) + return null; + int idx = bucketIndexFor(capacity); + Pool[] buckets = direct ? _direct : _indirect; + if (idx >= buckets.length) + return null; + return buckets[idx]; + } + + private int bucketIndexFor(int capacity) + { + return (capacity - 1) / _factor; + } + + @ManagedAttribute("The number of pooled direct ByteBuffers") + public long getDirectByteBufferCount() + { + return getByteBufferCount(true); + } + + @ManagedAttribute("The number of pooled heap ByteBuffers") + public long getHeapByteBufferCount() + { + return getByteBufferCount(false); + } + + private long getByteBufferCount(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(Pool::size).sum(); + } + + @ManagedAttribute("The number of pooled direct ByteBuffers that are available") + public long getAvailableDirectByteBufferCount() + { + return getAvailableByteBufferCount(true); + } + + @ManagedAttribute("The number of pooled heap ByteBuffers that are available") + public long getAvailableHeapByteBufferCount() + { + return getAvailableByteBufferCount(false); + } + + private long getAvailableByteBufferCount(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + return Arrays.stream(buckets).mapToLong(pool -> pool.values().stream().filter(Pool.Entry::isIdle).count()).sum(); + } + + @ManagedAttribute("The bytes retained by direct ByteBuffers") + public long getDirectMemory() + { + return getMemory(true); + } + + @ManagedAttribute("The bytes retained by heap ByteBuffers") + public long getHeapMemory() + { + return getMemory(false); + } + + private long getMemory(boolean direct) + { + if (direct) + return _currentDirectMemory.get(); + else + return _currentHeapMemory.get(); + } + + @ManagedAttribute("The available bytes retained by direct ByteBuffers") + public long getAvailableDirectMemory() + { + return getAvailableMemory(true); + } + + @ManagedAttribute("The available bytes retained by heap ByteBuffers") + public long getAvailableHeapMemory() + { + return getAvailableMemory(false); + } + + private long getAvailableMemory(boolean direct) + { + Pool[] buckets = direct ? _direct : _indirect; + long total = 0L; + for (int i = 0; i < buckets.length; i++) + { + Pool bucket = buckets[i]; + long capacity = (i + 1L) * _factor; + total += bucket.values().stream().filter(Pool.Entry::isIdle).count() * capacity; + } + return total; + } + + @ManagedOperation(value = "Clears this RetainableByteBufferPool", impact = "ACTION") + public void clear() + { + clearArray(_direct, _currentDirectMemory); + clearArray(_indirect, _currentHeapMemory); + } + + private void clearArray(Pool[] poolArray, AtomicLong memoryCounter) + { + for (Pool retainableByteBufferPool : poolArray) + { + for (Pool.Entry entry : retainableByteBufferPool.values()) + { + entry.remove(); + memoryCounter.addAndGet(-entry.getPooled().capacity()); + } + } + } + + private void releaseExcessMemory(boolean direct) + { + long maxMemory = direct ? _maxDirectMemory : _maxHeapMemory; + if (maxMemory > 0) + { + long excess = getMemory(direct) - maxMemory; + if (excess > 0) + evict(direct, excess); + } + } + + /** + * This eviction mechanism searches for the RetainableByteBuffers that were released the longest time ago. + * @param direct true to search in the direct buffers buckets, false to search in the heap buffers buckets. + * @param excess the amount of bytes to evict. At least this much will be removed from the buckets. + */ + private void evict(boolean direct, long excess) + { + if (LOG.isDebugEnabled()) + LOG.debug("evicting {} bytes from {} pools", excess, (direct ? "direct" : "heap")); + long now = System.nanoTime(); + long totalClearedCapacity = 0L; + + Pool[] buckets = direct ? _direct : _indirect; + + while (totalClearedCapacity < excess) + { + for (Pool bucket : buckets) + { + Pool.Entry oldestEntry = findOldestEntry(now, bucket); + if (oldestEntry == null) + continue; + + if (oldestEntry.remove()) + { + int clearedCapacity = oldestEntry.getPooled().capacity(); + if (direct) + _currentDirectMemory.addAndGet(-clearedCapacity); + else + _currentHeapMemory.addAndGet(-clearedCapacity); + totalClearedCapacity += clearedCapacity; + } + // else a concurrent thread evicted the same entry -> do not account for its capacity. + } + } + + if (LOG.isDebugEnabled()) + LOG.debug("eviction done, cleared {} bytes from {} pools", totalClearedCapacity, (direct ? "direct" : "heap")); + } + + private Pool.Entry findOldestEntry(long now, Pool bucket) + { + Pool.Entry oldestEntry = null; + for (Pool.Entry entry : bucket.values()) + { + if (oldestEntry != null) + { + long entryAge = now - entry.getPooled().getLastUpdate(); + if (entryAge > now - oldestEntry.getPooled().getLastUpdate()) + oldestEntry = entry; + } + else + { + oldestEntry = entry; + } + } + return oldestEntry; + } +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java index 1ab6d1ffb2ed..b760d00b8771 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBuffer.java @@ -15,32 +15,40 @@ import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Retainable; /** - * A Retainable ByteBuffer. - *

    Acquires a ByteBuffer from a {@link ByteBufferPool} and maintains a reference count that is - * initially 1, incremented with {@link #retain()} and decremented with {@link #release()}. The buffer - * is released to the pool when the reference count is decremented to 0.

    + *

    A pooled ByteBuffer which maintains a reference count that is + * incremented with {@link #retain()} and decremented with {@link #release()}. The buffer + * is released to the pool when {@link #release()} is called one more time than {@link #retain()}.

    + *

    A {@code RetainableByteBuffer} can either be: + *

      + *
    • in pool; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} throws {@link IllegalStateException}
    • + *
    • out of pool but not retained; in this case {@link #isRetained()} returns {@code false} and calling {@link #release()} returns {@code true}
    • + *
    • out of pool and retained; in this case {@link #isRetained()} returns {@code true} and calling {@link #release()} returns {@code false}
    • + *
    + *

    Calling {@link #release()} on a out of pool and retained instance does not re-pool it while that re-pools it on a out of pool but not retained instance.

    */ public class RetainableByteBuffer implements Retainable { - private final ByteBufferPool pool; private final ByteBuffer buffer; - private final AtomicInteger references; + private final AtomicInteger references = new AtomicInteger(); + private final Consumer releaser; + private final AtomicLong lastUpdate = new AtomicLong(System.nanoTime()); - public RetainableByteBuffer(ByteBufferPool pool, int size) + RetainableByteBuffer(ByteBuffer buffer, Consumer releaser) { - this(pool, size, false); + this.releaser = releaser; + this.buffer = buffer; } - public RetainableByteBuffer(ByteBufferPool pool, int size, boolean direct) + public int capacity() { - this.pool = pool; - this.buffer = pool.acquire(size, direct); - this.references = new AtomicInteger(1); + return buffer.capacity(); } public ByteBuffer getBuffer() @@ -48,32 +56,66 @@ public ByteBuffer getBuffer() return buffer; } - public int getReferences() + public long getLastUpdate() { - return references.get(); + return lastUpdate.getOpaque(); } + /** + * Checks if {@link #retain()} has been called at least one more time than {@link #release()}. + * @return true if this buffer is retained, false otherwise. + */ + public boolean isRetained() + { + return references.get() > 1; + } + + public boolean isDirect() + { + return buffer.isDirect(); + } + + /** + * Increments the retained counter of this buffer. It must be done internally by + * the pool right after creation and after each un-pooling. + * The reason why this method exists on top of {@link #retain()} is to be able to + * have some safety checks that must know why the ref counter is being incremented. + */ + void acquire() + { + if (references.getAndUpdate(c -> c == 0 ? 1 : c) != 0) + throw new IllegalStateException("re-pooled while still used " + this); + } + + /** + * Increments the retained counter of this buffer. + */ @Override public void retain() { - while (true) - { - int r = references.get(); - if (r == 0) - throw new IllegalStateException("released " + this); - if (references.compareAndSet(r, r + 1)) - break; - } + if (references.getAndUpdate(c -> c == 0 ? 0 : c + 1) == 0) + throw new IllegalStateException("released " + this); } - public int release() + /** + * Decrements the retained counter of this buffer. + * @return true if the buffer was re-pooled, false otherwise. + */ + public boolean release() { - int ref = references.decrementAndGet(); + int ref = references.updateAndGet(c -> + { + if (c == 0) + throw new IllegalStateException("already released " + this); + return c - 1; + }); if (ref == 0) - pool.release(buffer); - else if (ref < 0) - throw new IllegalStateException("already released " + this); - return ref; + { + lastUpdate.setOpaque(System.nanoTime()); + releaser.accept(buffer); + return true; + } + return false; } public int remaining() @@ -99,6 +141,6 @@ public void clear() @Override public String toString() { - return String.format("%s@%x{%s,r=%d}", getClass().getSimpleName(), hashCode(), BufferUtil.toDetailString(buffer), getReferences()); + return String.format("%s@%x{%s,r=%d}", getClass().getSimpleName(), hashCode(), BufferUtil.toDetailString(buffer), references.get()); } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java new file mode 100644 index 000000000000..a4bd9d3754c2 --- /dev/null +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -0,0 +1,58 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.util.component.Container; + +/** + *

    A {@link RetainableByteBuffer} pool.

    + *

    Acquired buffers must be released by calling {@link RetainableByteBuffer#release()} otherwise the memory they hold will + * be leaked.

    + */ +public interface RetainableByteBufferPool +{ + /** + * Acquires a memory buffer from the pool. + * @param size The size of the buffer. The returned buffer will have at least this capacity. + * @param direct true if a direct memory buffer is needed, false otherwise. + * @return a memory buffer. + */ + RetainableByteBuffer acquire(int size, boolean direct); + + /** + * Finds a {@link RetainableByteBufferPool} implementation in the given container, or wrap the given + * {@link ByteBufferPool} with an adapter. + * @param container the container to search for an existing memory pool. + * @param byteBufferPool the {@link ByteBufferPool} to wrap if no memory pool was found in the container. + * @return the {@link RetainableByteBufferPool} found or the wrapped one. + */ + static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool byteBufferPool) + { + RetainableByteBufferPool retainableByteBufferPool = container == null ? null : container.getBean(RetainableByteBufferPool.class); + if (retainableByteBufferPool == null) + { + // Wrap the ByteBufferPool instance. + retainableByteBufferPool = (size, direct) -> + { + ByteBuffer byteBuffer = byteBufferPool.acquire(size, direct); + RetainableByteBuffer retainableByteBuffer = new RetainableByteBuffer(byteBuffer, byteBufferPool::release); + retainableByteBuffer.acquire(); + return retainableByteBuffer; + }; + } + return retainableByteBufferPool; + } +} diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index ea896ee7f1ee..728d8eb0ad9b 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -35,6 +35,8 @@ import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -107,10 +109,11 @@ private enum FlushState private final AtomicLong _bytesIn = new AtomicLong(); private final AtomicLong _bytesOut = new AtomicLong(); private final ByteBufferPool _bufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final SSLEngine _sslEngine; private final DecryptedEndPoint _decryptedEndPoint; private ByteBuffer _decryptedInput; - private ByteBuffer _encryptedInput; + private RetainableByteBuffer _encryptedInput; private ByteBuffer _encryptedOutput; private final boolean _encryptedDirectBuffers; private final boolean _decryptedDirectBuffers; @@ -187,11 +190,18 @@ public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint public SslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) + { + this(RetainableByteBufferPool.findOrAdapt(null, byteBufferPool), byteBufferPool, executor, endPoint, sslEngine, useDirectBuffersForEncryption, useDirectBuffersForDecryption); + } + + public SslConnection(RetainableByteBufferPool retainableByteBufferPool, ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine sslEngine, + boolean useDirectBuffersForEncryption, boolean useDirectBuffersForDecryption) { // This connection does not execute calls to onFillable(), so they will be called by the selector thread. // onFillable() does not block and will only wakeup another thread to do the actual reading and handling. super(endPoint, executor); this._bufferPool = byteBufferPool; + this._retainableByteBufferPool = retainableByteBufferPool; this._sslEngine = sslEngine; this._decryptedEndPoint = newDecryptedEndPoint(); this._encryptedDirectBuffers = useDirectBuffersForEncryption; @@ -326,7 +336,7 @@ private int getBufferSize(ToIntFunction bufferSizeFn) private void acquireEncryptedInput() { if (_encryptedInput == null) - _encryptedInput = _bufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); + _encryptedInput = _retainableByteBufferPool.acquire(getPacketBufferSize(), _encryptedDirectBuffers); } private void acquireEncryptedOutput() @@ -339,7 +349,7 @@ private void acquireEncryptedOutput() public void onUpgradeTo(ByteBuffer buffer) { acquireEncryptedInput(); - BufferUtil.append(_encryptedInput, buffer); + BufferUtil.append(_encryptedInput.getBuffer(), buffer); } @Override @@ -409,7 +419,7 @@ protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuff @Override public String toConnectionString() { - ByteBuffer b = _encryptedInput; + ByteBuffer b = _encryptedInput == null ? null : _encryptedInput.getBuffer(); int ei = b == null ? -1 : b.remaining(); b = _encryptedOutput; int eo = b == null ? -1 : b.remaining(); @@ -431,7 +441,7 @@ private void releaseEncryptedInputBuffer() { if (_encryptedInput != null && !_encryptedInput.hasRemaining()) { - _bufferPool.release(_encryptedInput); + _encryptedInput.release(); _encryptedInput = null; } } @@ -672,14 +682,14 @@ public int fill(ByteBuffer buffer) throws IOException } // Let's try reading some encrypted data... even if we have some already. - int netFilled = networkFill(_encryptedInput); + int netFilled = networkFill(_encryptedInput.getBuffer()); if (netFilled > 0) _bytesIn.addAndGet(netFilled); if (LOG.isDebugEnabled()) LOG.debug("net filled={}", netFilled); // Workaround for Java 11 behavior. - if (netFilled < 0 && isHandshakeInitial() && BufferUtil.isEmpty(_encryptedInput)) + if (netFilled < 0 && isHandshakeInitial() && (_encryptedInput == null || _encryptedInput.isEmpty())) closeInbound(); if (netFilled > 0 && !isHandshakeComplete() && isOutboundDone()) @@ -698,7 +708,7 @@ public int fill(ByteBuffer buffer) throws IOException try { _underflown = false; - unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput, appIn); + unwrapResult = SslConnection.this.unwrap(_sslEngine, _encryptedInput.getBuffer(), appIn); } finally { @@ -708,7 +718,7 @@ public int fill(ByteBuffer buffer) throws IOException LOG.debug("unwrap net_filled={} {} encryptedBuffer={} unwrapBuffer={} appBuffer={}", netFilled, StringUtil.replace(unwrapResult.toString(), '\n', ' '), - BufferUtil.toSummaryString(_encryptedInput), + _encryptedInput, BufferUtil.toDetailString(appIn), BufferUtil.toDetailString(buffer)); @@ -729,13 +739,13 @@ public int fill(ByteBuffer buffer) throws IOException case BUFFER_UNDERFLOW: // Continue if we can compact? - if (BufferUtil.compact(_encryptedInput)) + if (BufferUtil.compact(_encryptedInput.getBuffer())) continue; // Are we out of space? - if (BufferUtil.space(_encryptedInput) == 0) + if (BufferUtil.space(_encryptedInput.getBuffer()) == 0) { - BufferUtil.clear(_encryptedInput); + BufferUtil.clear(_encryptedInput.getBuffer()); throw new SSLHandshakeException("Encrypted buffer max length exceeded"); } @@ -847,7 +857,7 @@ protected void needsFillInterest() _flushState, _fillState, _underflown, - BufferUtil.toDetailString(_encryptedInput), + _encryptedInput, BufferUtil.toDetailString(_decryptedInput), SslConnection.this); @@ -855,7 +865,7 @@ protected void needsFillInterest() return; // Fillable if we have decrypted input OR enough encrypted input. - fillable = BufferUtil.hasContent(_decryptedInput) || (BufferUtil.hasContent(_encryptedInput) && !_underflown); + fillable = BufferUtil.hasContent(_decryptedInput) || (_encryptedInput != null && _encryptedInput.hasRemaining() && !_underflown); HandshakeStatus status = _sslEngine.getHandshakeStatus(); switch (status) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java new file mode 100644 index 000000000000..9dba1740f57d --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -0,0 +1,316 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ArrayRetainableByteBufferPoolTest +{ + @Test + public void testMaxMemoryEviction() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, Integer.MAX_VALUE, 40, 40); + + List buffers = new ArrayList<>(); + + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(10, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + buffers.add(pool.acquire(20, true)); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getDirectMemory(), greaterThan(0L)); + + buffers.forEach(RetainableByteBuffer::release); + + assertThat(pool.getAvailableDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), lessThan((long)buffers.size())); + assertThat(pool.getDirectByteBufferCount(), greaterThan(0L)); + assertThat(pool.getDirectByteBufferCount(), lessThan((long)buffers.size())); + assertThat(pool.getDirectMemory(), lessThanOrEqualTo(40L)); + assertThat(pool.getDirectMemory(), greaterThan(0L)); + } + + @Test + public void testBelowMinCapacityDoesNotPool() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + RetainableByteBuffer buf1 = pool.acquire(1, true); + assertThat(buf1.capacity(), is(1)); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + + buf1.release(); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + } + + @Test + public void testOverMaxCapacityDoesNotPool() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + RetainableByteBuffer buf1 = pool.acquire(21, true); + assertThat(buf1.capacity(), is(21)); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + + buf1.release(); + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + } + + @Test + public void testRetain() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + RetainableByteBuffer buf1 = pool.acquire(10, true); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + assertThat(buf1.isRetained(), is(false)); + buf1.retain(); + buf1.retain(); + assertThat(buf1.isRetained(), is(true)); + assertThat(buf1.release(), is(false)); + assertThat(buf1.isRetained(), is(true)); + assertThat(buf1.release(), is(false)); + assertThat(buf1.isRetained(), is(false)); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + assertThat(buf1.release(), is(true)); + assertThat(buf1.isRetained(), is(false)); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + } + + @Test + public void testTooManyReleases() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + RetainableByteBuffer buf1 = pool.acquire(10, true); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + buf1.release(); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + + assertThrows(IllegalStateException.class, buf1::release); + + assertThat(pool.getDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectMemory(), is(10L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + } + + @Test + public void testMaxBucketSize() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, 2); + + RetainableByteBuffer buf1 = pool.acquire(1, true); // pooled + assertThat(buf1.capacity(), is(10)); + RetainableByteBuffer buf2 = pool.acquire(1, true); // pooled + assertThat(buf2.capacity(), is(10)); + RetainableByteBuffer buf3 = pool.acquire(1, true); // not pooled, bucket is full + assertThat(buf3.capacity(), is(1)); + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(20L)); + + RetainableByteBuffer buf4 = pool.acquire(11, true); // pooled + assertThat(buf4.capacity(), is(20)); + RetainableByteBuffer buf5 = pool.acquire(11, true); // pooled + assertThat(buf5.capacity(), is(20)); + RetainableByteBuffer buf6 = pool.acquire(11, true); // not pooled, bucket is full + assertThat(buf6.capacity(), is(11)); + + assertThat(pool.getDirectByteBufferCount(), is(4L)); + assertThat(pool.getDirectMemory(), is(60L)); + } + + @Test + public void testBufferReleaseRepools() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(0, 10, 20, 1); + + List all = new ArrayList<>(); + + all.add(pool.acquire(1, true)); // pooled + all.add(pool.acquire(1, true)); // not pooled, bucket is full + all.add(pool.acquire(11, true)); // pooled + all.add(pool.acquire(11, true)); // not pooled, bucket is full + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + + all.forEach(RetainableByteBuffer::release); + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(2L)); + assertThat(pool.getAvailableDirectMemory(), is(30L)); + } + + @Test + public void testFactorAndCapacity() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(10, 10, 20, Integer.MAX_VALUE); + + pool.acquire(1, true); // not pooled, < minCapacity + pool.acquire(10, true); // pooled + pool.acquire(20, true); // pooled + pool.acquire(30, true); // not pooled, > maxCapacity + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(30L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + } + + @Test + public void testClearUnlinksLeakedBuffers() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); + + pool.acquire(10, true); + pool.acquire(10, true); + + assertThat(pool.getDirectByteBufferCount(), is(2L)); + assertThat(pool.getDirectMemory(), is(2048L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + + pool.clear(); + + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(pool.getAvailableDirectMemory(), is(0L)); + } + + @Test + public void testRetainAfterRePooledThrows() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); + RetainableByteBuffer buf1 = pool.acquire(10, true); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(buf1.release(), is(true)); + assertThrows(IllegalStateException.class, buf1::retain); + assertThrows(IllegalStateException.class, buf1::release); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + + // check that the buffer is still available + RetainableByteBuffer buf2 = pool.acquire(10, true); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(0L)); + assertThat(buf2 == buf1, is(true)); // make sure it's not a new instance + assertThat(buf1.release(), is(true)); + assertThat(pool.getDirectByteBufferCount(), is(1L)); + assertThat(pool.getAvailableDirectByteBufferCount(), is(1L)); + } + + @Test + public void testAcquireRelease() + { + ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool(); + + for (int i = 0; i < 3; i++) + { + RetainableByteBuffer buf1 = pool.acquire(10, true); + assertThat(buf1, is(notNullValue())); + assertThat(buf1.capacity(), is(1024)); + RetainableByteBuffer buf2 = pool.acquire(10, true); + assertThat(buf2, is(notNullValue())); + assertThat(buf2.capacity(), is(1024)); + buf1.release(); + buf2.release(); + + RetainableByteBuffer buf3 = pool.acquire(16384 + 1, true); + assertThat(buf3, is(notNullValue())); + assertThat(buf3.capacity(), is(16384 + 1024)); + buf3.release(); + + RetainableByteBuffer buf4 = pool.acquire(32768, true); + assertThat(buf4, is(notNullValue())); + assertThat(buf4.capacity(), is(32768)); + buf4.release(); + + RetainableByteBuffer buf5 = pool.acquire(32768, false); + assertThat(buf5, is(notNullValue())); + assertThat(buf5.capacity(), is(32768)); + buf5.release(); + } + + assertThat(pool.getDirectByteBufferCount(), is(4L)); + assertThat(pool.getHeapByteBufferCount(), is(1L)); + assertThat(pool.getDirectMemory(), is(1024 + 1024 + 16384 + 1024 + 32768L)); + assertThat(pool.getHeapMemory(), is(32768L)); + + pool.clear(); + + assertThat(pool.getDirectByteBufferCount(), is(0L)); + assertThat(pool.getHeapByteBufferCount(), is(0L)); + assertThat(pool.getDirectMemory(), is(0L)); + assertThat(pool.getHeapMemory(), is(0L)); + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index 7903f7f65ab5..0a020a44c010 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -32,8 +32,10 @@ import java.util.stream.Collectors; import org.eclipse.jetty.io.ArrayByteBufferPool; +import org.eclipse.jetty.io.ArrayRetainableByteBufferPool; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.StringUtil; @@ -188,6 +190,8 @@ public AbstractConnector( pool = _server.getBean(ByteBufferPool.class); _byteBufferPool = pool != null ? pool : new ArrayByteBufferPool(); addBean(_byteBufferPool); + RetainableByteBufferPool retainableByteBufferPool = _server.getBean(RetainableByteBufferPool.class); + addBean(retainableByteBufferPool == null ? new ArrayRetainableByteBufferPool() : retainableByteBufferPool, retainableByteBufferPool == null); addEventListener(new Container.Listener() { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 728214a75721..b0fb50dc6615 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -17,7 +17,6 @@ import java.nio.ByteBuffer; import java.nio.channels.WritePendingException; import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.LongAdder; import org.eclipse.jetty.http.BadMessageException; @@ -35,6 +34,8 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; +import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; @@ -56,12 +57,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http private final HttpConfiguration _config; private final Connector _connector; private final ByteBufferPool _bufferPool; + private final RetainableByteBufferPool _retainableByteBufferPool; private final HttpInput _input; private final HttpGenerator _generator; private final HttpChannelOverHttp _channel; private final HttpParser _parser; - private final AtomicInteger _contentBufferReferences = new AtomicInteger(); - private volatile ByteBuffer _requestBuffer = null; + private volatile RetainableByteBuffer _retainableByteBuffer; private final AsyncReadCallback _asyncReadCallback = new AsyncReadCallback(); private final SendCallback _sendCallback = new SendCallback(); private final boolean _recordHttpComplianceViolations; @@ -96,6 +97,7 @@ public HttpConnection(HttpConfiguration config, Connector connector, EndPoint en _config = config; _connector = connector; _bufferPool = _connector.getByteBufferPool(); + _retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, _bufferPool); _generator = newHttpGenerator(); _channel = newHttpChannel(); _input = _channel.getRequest().getHttpInput(); @@ -198,10 +200,10 @@ public void setUseOutputDirectByteBuffers(boolean useOutputDirectByteBuffers) @Override public ByteBuffer onUpgradeFrom() { - if (BufferUtil.hasContent(_requestBuffer)) + if (!isRequestBufferEmpty()) { - ByteBuffer unconsumed = ByteBuffer.allocateDirect(_requestBuffer.remaining()); - unconsumed.put(_requestBuffer); + ByteBuffer unconsumed = ByteBuffer.allocateDirect(_retainableByteBuffer.remaining()); + unconsumed.put(_retainableByteBuffer.getBuffer()); unconsumed.flip(); releaseRequestBuffer(); return unconsumed; @@ -225,36 +227,34 @@ public void onFlushed(long bytes) throws IOException void releaseRequestBuffer() { - if (_requestBuffer != null && !_requestBuffer.hasRemaining()) + if (_retainableByteBuffer != null && !_retainableByteBuffer.hasRemaining()) { if (LOG.isDebugEnabled()) LOG.debug("releaseRequestBuffer {}", this); - ByteBuffer buffer = _requestBuffer; - _requestBuffer = null; - _bufferPool.release(buffer); + if (_retainableByteBuffer.release()) + _retainableByteBuffer = null; + else + throw new IllegalStateException("unreleased buffer " + _retainableByteBuffer); } } - public ByteBuffer getRequestBuffer() + private ByteBuffer getRequestBuffer() { - if (_requestBuffer == null) - { - boolean useDirectByteBuffers = isUseInputDirectByteBuffers(); - _requestBuffer = _bufferPool.acquire(getInputBufferSize(), useDirectByteBuffers); - } - return _requestBuffer; + if (_retainableByteBuffer == null) + _retainableByteBuffer = _retainableByteBufferPool.acquire(getInputBufferSize(), isUseInputDirectByteBuffers()); + return _retainableByteBuffer.getBuffer(); } public boolean isRequestBufferEmpty() { - return BufferUtil.isEmpty(_requestBuffer); + return _retainableByteBuffer == null || _retainableByteBuffer.isEmpty(); } @Override public void onFillable() { if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} onFillable enter {} {}", this, _channel.getState(), _retainableByteBuffer); HttpConnection last = setCurrentConnection(this); try @@ -300,17 +300,26 @@ else if (filled < 0) } catch (Throwable x) { - if (LOG.isDebugEnabled()) - LOG.debug("{} caught exception {}", this, _channel.getState(), x); - BufferUtil.clear(_requestBuffer); - releaseRequestBuffer(); - getEndPoint().close(x); + try + { + if (LOG.isDebugEnabled()) + LOG.debug("{} caught exception {}", this, _channel.getState(), x); + if (_retainableByteBuffer != null) + { + _retainableByteBuffer.clear(); + releaseRequestBuffer(); + } + } + finally + { + getEndPoint().close(x); + } } finally { setCurrentConnection(last); if (LOG.isDebugEnabled()) - LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} onFillable exit {} {}", this, _channel.getState(), _retainableByteBuffer); } } @@ -338,22 +347,22 @@ void parseAndFillForContent() private int fillRequestBuffer() { - if (_contentBufferReferences.get() > 0) + if (_retainableByteBuffer != null && _retainableByteBuffer.isRetained()) throw new IllegalStateException("fill with unconsumed content on " + this); - if (BufferUtil.isEmpty(_requestBuffer)) + if (isRequestBufferEmpty()) { // Get a buffer // We are not in a race here for the request buffer as we have not yet received a request, // so there are not an possible legal threads calling #parseContent or #completed. - _requestBuffer = getRequestBuffer(); + ByteBuffer requestBuffer = getRequestBuffer(); // fill try { - int filled = getEndPoint().fill(_requestBuffer); + int filled = getEndPoint().fill(requestBuffer); if (filled == 0) // Do a retry on fill 0 (optimization for SSL connections) - filled = getEndPoint().fill(_requestBuffer); + filled = getEndPoint().fill(requestBuffer); if (filled > 0) bytesIn.add(filled); @@ -361,7 +370,7 @@ else if (filled < 0) _parser.atEOF(); if (LOG.isDebugEnabled()) - LOG.debug("{} filled {} {}", this, filled, BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} filled {} {}", this, filled, _retainableByteBuffer); return filled; } @@ -379,15 +388,15 @@ else if (filled < 0) private boolean parseRequestBuffer() { if (LOG.isDebugEnabled()) - LOG.debug("{} parse {}", this, BufferUtil.toDetailString(_requestBuffer)); + LOG.debug("{} parse {}", this, _retainableByteBuffer); - boolean handle = _parser.parseNext(_requestBuffer == null ? BufferUtil.EMPTY_BUFFER : _requestBuffer); + boolean handle = _parser.parseNext(_retainableByteBuffer == null ? BufferUtil.EMPTY_BUFFER : _retainableByteBuffer.getBuffer()); if (LOG.isDebugEnabled()) LOG.debug("{} parsed {} {}", this, handle, _parser); // recycle buffer ? - if (_contentBufferReferences.get() == 0) + if (_retainableByteBuffer != null && !_retainableByteBuffer.isRetained()) releaseRequestBuffer(); return handle; @@ -406,15 +415,17 @@ private boolean upgrade() _channel.recycle(); _parser.reset(); _generator.reset(); - if (_contentBufferReferences.get() == 0) + if (_retainableByteBuffer != null) { - releaseRequestBuffer(); - } - else - { - LOG.warn("{} lingering content references?!?!", this); - _requestBuffer = null; // Not returned to pool! - _contentBufferReferences.set(0); + if (!_retainableByteBuffer.isRetained()) + { + releaseRequestBuffer(); + } + else + { + LOG.warn("{} lingering content references?!?!", this); + _retainableByteBuffer = null; // Not returned to pool! + } } return true; } @@ -472,7 +483,7 @@ else if (_generator.isPersistent() && !complete) if (_parser.isStart()) { // if the buffer is empty - if (BufferUtil.isEmpty(_requestBuffer)) + if (isRequestBufferEmpty()) { // look for more data fillInterested(); @@ -629,21 +640,13 @@ private class Content extends HttpInput.Content public Content(ByteBuffer content) { super(content); - _contentBufferReferences.incrementAndGet(); + _retainableByteBuffer.retain(); } @Override public void succeeded() { - int counter = _contentBufferReferences.decrementAndGet(); - if (counter == 0) - releaseRequestBuffer(); - // TODO: this should do something (warn? fail?) if _contentBufferReferences goes below 0 - if (counter < 0) - { - LOG.warn("Content reference counting went below zero: {}", counter); - _contentBufferReferences.incrementAndGet(); - } + _retainableByteBuffer.release(); } @Override diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java index f73ce1f087a6..97dd019ffaf7 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/SslConnectionFactory.java @@ -21,8 +21,10 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.io.ssl.SslHandshakeListener; import org.eclipse.jetty.util.annotation.Name; @@ -165,7 +167,9 @@ public Connection newConnection(Connector connector, EndPoint endPoint) protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { - return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + return new SslConnection(retainableByteBufferPool, byteBufferPool, connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption()); } @Override diff --git a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java index cc45b3f001a0..9182681390ee 100644 --- a/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java +++ b/jetty-websocket/websocket-core-client/src/main/java/org/eclipse/jetty/websocket/core/client/CoreClientUpgradeRequest.java @@ -39,6 +39,7 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.QuotedStringTokenizer; @@ -439,7 +440,8 @@ else if (values.length == 1) HttpClient httpClient = wsClient.getHttpClient(); ByteBufferPool bufferPool = wsClient.getWebSocketComponents().getBufferPool(); - WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, coreSession); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(wsClient.getWebSocketComponents(), bufferPool); + WebSocketConnection wsConnection = new WebSocketConnection(endPoint, httpClient.getExecutor(), httpClient.getScheduler(), bufferPool, retainableByteBufferPool, coreSession); wsClient.getEventListeners().forEach(wsConnection::addEventListener); coreSession.setWebSocketConnection(wsConnection); Exception listenerError = notifyUpgradeListeners((listener) -> listener.onHandshakeResponse(this, response)); diff --git a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index dc796cc102a1..3c14c78075f5 100644 --- a/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core-common/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.RetainableByteBuffer; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.component.Dumpable; @@ -53,6 +54,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio private final AutoLock lock = new AutoLock(); private final ByteBufferPool bufferPool; + private final RetainableByteBufferPool retainableByteBufferPool; private final Generator generator; private final Parser parser; private final WebSocketCoreSession coreSession; @@ -78,9 +80,10 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, + RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { - this(endp, executor, scheduler, bufferPool, coreSession, null); + this(endp, executor, scheduler, bufferPool, retainableByteBufferPool, coreSession, null); } /** @@ -93,6 +96,7 @@ public WebSocketConnection(EndPoint endp, * @param executor A thread executor to use for WS callbacks. * @param scheduler A scheduler to use for timeouts * @param bufferPool A pool of buffers to use. + * @param retainableByteBufferPool A pool of retainable buffers to use. * @param coreSession The WC core session to which frames are delivered. * @param randomMask A Random used to mask frames. If null then SecureRandom will be created if needed. */ @@ -100,6 +104,7 @@ public WebSocketConnection(EndPoint endp, Executor executor, Scheduler scheduler, ByteBufferPool bufferPool, + RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession, Random randomMask) { @@ -109,8 +114,10 @@ public WebSocketConnection(EndPoint endp, Objects.requireNonNull(coreSession, "Session"); Objects.requireNonNull(executor, "Executor"); Objects.requireNonNull(bufferPool, "ByteBufferPool"); + Objects.requireNonNull(retainableByteBufferPool, "RetainableByteBufferPool"); this.bufferPool = bufferPool; + this.retainableByteBufferPool = retainableByteBufferPool; this.coreSession = coreSession; this.generator = new Generator(); this.parser = new Parser(bufferPool, coreSession); @@ -310,7 +317,7 @@ private void reacquireNetworkBuffer() private RetainableByteBuffer newNetworkBuffer(int capacity) { - return new RetainableByteBuffer(bufferPool, capacity, isUseInputDirectByteBuffers()); + return retainableByteBufferPool.acquire(capacity, isUseInputDirectByteBuffers()); } private void releaseNetworkBuffer() @@ -464,7 +471,7 @@ private void fillAndParse() } // If more references that 1(us), don't refill into buffer and risk compaction. - if (networkBuffer.getReferences() > 1) + if (networkBuffer.isRetained()) reacquireNetworkBuffer(); int filled = getEndPoint().fill(networkBuffer.getBuffer()); // TODO check if compact is possible. diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index a28c98983f61..734091f247e9 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -24,6 +24,7 @@ import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpTransport; @@ -216,9 +217,9 @@ protected void handle(Runnable runnable) protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession); - protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, WebSocketCoreSession coreSession) + protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, RetainableByteBufferPool retainableByteBufferPool, WebSocketCoreSession coreSession) { - return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, coreSession); + return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, retainableByteBufferPool, coreSession); } protected abstract void prepareResponse(Response response, WebSocketNegotiation negotiation); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java index 041b19e92525..351e7cc14eda 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC6455Handshaker.java @@ -23,6 +23,8 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.PreEncodedHttpField; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -94,7 +96,9 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web { HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); - return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } @Override diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java index 05892c948dc8..b2bd67c9acf2 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/RFC8441Handshaker.java @@ -19,7 +19,9 @@ import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.RetainableByteBufferPool; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.Request; @@ -78,7 +80,9 @@ protected WebSocketConnection createWebSocketConnection(Request baseRequest, Web HttpChannel httpChannel = baseRequest.getHttpChannel(); Connector connector = httpChannel.getConnector(); EndPoint endPoint = httpChannel.getTunnellingEndPoint(); - return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession); + ByteBufferPool byteBufferPool = connector.getByteBufferPool(); + RetainableByteBufferPool retainableByteBufferPool = RetainableByteBufferPool.findOrAdapt(connector, byteBufferPool); + return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), byteBufferPool, retainableByteBufferPool, coreSession); } @Override From 42d4a9864ad44416dbef7393f08792efad14e60a Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 13 Jul 2021 11:18:15 +1000 Subject: [PATCH 69/75] Improve #6322 log buckets in RetainableByteBufferPool Dumpable Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 35 +++++++++++++++++-- .../io/ArrayRetainableByteBufferPoolTest.java | 2 ++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index f740c9486d82..e5247d1ef3ac 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -13,6 +13,7 @@ package org.eclipse.jetty.io; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.atomic.AtomicLong; @@ -25,11 +26,13 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.eclipse.jetty.util.component.Dumpable; +import org.eclipse.jetty.util.component.DumpableCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @ManagedObject -public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool +public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool, Dumpable { private static final Logger LOG = LoggerFactory.getLogger(ArrayRetainableByteBufferPool.class); @@ -43,7 +46,6 @@ public class ArrayRetainableByteBufferPool implements RetainableByteBufferPool private final AtomicLong _currentHeapMemory = new AtomicLong(); private final AtomicLong _currentDirectMemory = new AtomicLong(); private final Function _bucketIndexFor; - private final Function _bucketCapacity; public ArrayRetainableByteBufferPool() { @@ -92,7 +94,6 @@ protected ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapa _direct = directArray; _indirect = indirectArray; _bucketIndexFor = bucketIndexFor; - _bucketCapacity = bucketCapacity; } public int getMinCapacity() @@ -314,6 +315,28 @@ private void evict(boolean direct, long excess) LOG.debug("eviction done, cleared {} bytes from {} pools", totalClearedCapacity, (direct ? "direct" : "heap")); } + @Override + public String toString() + { + return String.format("%s{min=%d,max=%d,buckets=%d,heap=%d/%d,direct=%d/%d}", + super.toString(), + _minCapacity, _maxCapacity, + _direct.length, + _currentHeapMemory.get(), _maxHeapMemory, + _currentDirectMemory.get(), _maxDirectMemory); + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects( + out, + indent, + this, + DumpableCollection.fromArray("direct", _direct), + DumpableCollection.fromArray("indirect", _indirect)); + } + private Pool.Entry findOldestEntry(long now, Pool bucket) { Pool.Entry oldestEntry = null; @@ -342,6 +365,12 @@ private static class Bucket extends Pool super(Pool.StrategyType.THREAD_ID, size, true); _capacity = capacity; } + + @Override + public String toString() + { + return String.format("%s{capacity=%d}", super.toString(), _capacity); + } } /** diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index fc6858c5eef8..ee7b95723b4f 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -340,5 +340,7 @@ public void testLogBuckets() capacity = capacity * 2; } + + System.err.println(pool.dump()); } } From d607f21742b1361275433488132e0951ee5393a9 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 13 Jul 2021 11:53:03 +1000 Subject: [PATCH 70/75] Improve #6322 log buckets in RetainableByteBufferPool Sampled Pool Signed-off-by: Greg Wilkins --- .../jetty/io/RetainableByteBufferPool.java | 57 +++++++++++++++++++ .../io/ArrayRetainableByteBufferPoolTest.java | 7 ++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index a4bd9d3754c2..76595c161a6c 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -13,9 +13,14 @@ package org.eclipse.jetty.io; +import java.io.IOException; import java.nio.ByteBuffer; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Container; +import org.eclipse.jetty.util.component.Dumpable; +import org.eclipse.jetty.util.statistic.SampleStatistic; /** *

    A {@link RetainableByteBuffer} pool.

    @@ -55,4 +60,56 @@ static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool } return retainableByteBufferPool; } + + /** + * A Pool wrapper that collects sample statistics on the acquired + * buffers. + * @see SampleStatistic + */ + @ManagedObject + class SampledPool implements RetainableByteBufferPool, Dumpable + { + private final RetainableByteBufferPool _pool; + private final SampleStatistic _heap = new SampleStatistic(); + private final SampleStatistic _direct = new SampleStatistic(); + + public SampledPool(RetainableByteBufferPool pool) + { + _pool = pool; + } + + @ManagedAttribute("Heap acquire samples") + public SampleStatistic getHeapSample() + { + return _heap; + } + + @ManagedAttribute("Direct acquire samples") + public SampleStatistic getDirectSample() + { + return _direct; + } + + @Override + public RetainableByteBuffer acquire(int size, boolean direct) + { + (direct ? _direct : _heap).record(size); + return _pool.acquire(size, direct); + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects(out, indent, this, + Dumpable.named("heap", _heap), + Dumpable.named("direct", _direct), + _pool); + } + + @ManagedAttribute("Pool") + public RetainableByteBufferPool getPool() + { + return _pool; + } + } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index ee7b95723b4f..d181b69daa37 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -13,6 +13,7 @@ package org.eclipse.jetty.io; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -316,9 +317,9 @@ public void testAcquireRelease() } @Test - public void testLogBuckets() + public void testLogBuckets() throws IOException { - ArrayRetainableByteBufferPool pool = new ArrayRetainableByteBufferPool.LogBuckets(); + RetainableByteBufferPool.SampledPool pool = new RetainableByteBufferPool.SampledPool(new ArrayRetainableByteBufferPool.LogBuckets()); assertThat(pool.acquire(1, false).capacity(), is(1)); assertThat(pool.acquire(2, false).capacity(), is(2)); assertThat(pool.acquire(3, false).capacity(), is(4)); @@ -332,7 +333,7 @@ public void testLogBuckets() b = pool.acquire(capacity, false); assertThat(b.capacity(), Matchers.is(capacity)); - if (capacity >= pool.getMaxCapacity()) + if (capacity >= ((ArrayRetainableByteBufferPool)pool.getPool()).getMaxCapacity()) break; b = pool.acquire(capacity + 1, false); From ed11f6c704e0d5e3254b66637a09a6b81655115c Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 16 Jul 2021 18:41:33 +1000 Subject: [PATCH 71/75] Merge remote-tracking branch 'origin/jetty-10.0.x-6322-UseRetainableByteBuffer' into jetty-10.0.x-6322-UseRetainableByteBuffer-LogBuckets Updates from review Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 11 ++-- .../io/ArrayRetainableByteBufferPoolTest.java | 4 +- .../java/org/eclipse/jetty/util/TypeUtil.java | 38 ------------- .../org/eclipse/jetty/util/TypeUtilTest.java | 54 ------------------- 4 files changed, 7 insertions(+), 100 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index ff391a407b87..2f812aa3e708 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -22,7 +22,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Pool; -import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; @@ -382,19 +381,19 @@ public String toString() * uses buckets of buffers that increase in size by a power of * 2 (eg 1k, 2k, 4k, 8k, etc.). */ - public static class LogBuckets extends ArrayRetainableByteBufferPool + public static class ExponentialPool extends ArrayRetainableByteBufferPool { - public LogBuckets() + public ExponentialPool() { this(0, -1, Integer.MAX_VALUE); } - public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize) + public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize) { this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); } - public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) { super(minCapacity, -1, @@ -402,7 +401,7 @@ public LogBuckets(int minCapacity, int maxCapacity, int maxBucketSize, long maxH maxBucketSize, maxHeapMemory, maxDirectMemory, - TypeUtil::log2NextPowerOf2, + c -> 32 - Integer.numberOfLeadingZeros(c - 1), i -> 1 << i); } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index d181b69daa37..87a69649839d 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -317,9 +317,9 @@ public void testAcquireRelease() } @Test - public void testLogBuckets() throws IOException + public void testSampledExponentialPool() throws IOException { - RetainableByteBufferPool.SampledPool pool = new RetainableByteBufferPool.SampledPool(new ArrayRetainableByteBufferPool.LogBuckets()); + RetainableByteBufferPool.SampledPool pool = new RetainableByteBufferPool.SampledPool(new ArrayRetainableByteBufferPool.ExponentialPool()); assertThat(pool.acquire(1, false).capacity(), is(1)); assertThat(pool.acquire(2, false).capacity(), is(2)); assertThat(pool.acquire(3, false).capacity(), is(4)); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index 2302ad4ee925..5d937f809d1e 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -800,42 +800,4 @@ public static Stream> serviceProviderStream(Servic { return StreamSupport.stream(new ServiceLoaderSpliterator<>(serviceLoader), false); } - - /** - * Round up to the next power of 2. - * @param v An integer > 0 and <= half of {@link Integer#MAX_VALUE} - * @return The next power of two that is equal too or larger than the passed integer. - */ - public static int nextPowerOf2(int v) - { - if (v < 0 || v > (Integer.MAX_VALUE / 2)) - throw new IllegalArgumentException(Integer.toString(v)); - - // This algorithm is from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - // and gives good performance on most architectures. - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; - } - - private static final int[] MultiplyDeBruijnBitPosition2 = - { - 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 - }; - - /** - * The log2 of the value rounded up to the next power of 2. - * @param v An integer > 0 and <= half of {@link Integer#MAX_VALUE} - * @return The log2 of next power of two that is equal too or larger than the passed integer. - */ - public static int log2NextPowerOf2(int v) - { - // This algorithm is from https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn - return MultiplyDeBruijnBitPosition2[(int)((0xFFFFFFFFL & (nextPowerOf2(v) * 0x077CB531L)) >> 27)]; - } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java index 7bf6222fd529..b37a0f67fa92 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java @@ -24,7 +24,6 @@ import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -188,57 +187,4 @@ public void testGetLocationJavaLangThreadDeathJPMS() String expectedJavaBase = "/java.base"; assertThat(TypeUtil.getLocationOfClass(java.lang.ThreadDeath.class).toASCIIString(), containsString(expectedJavaBase)); } - - @Test - public void testNextPowerOf2() - { - assertThat(TypeUtil.nextPowerOf2(0), Matchers.is(0)); - assertThat(TypeUtil.nextPowerOf2(1), Matchers.is(1)); - assertThat(TypeUtil.nextPowerOf2(2), Matchers.is(2)); - assertThat(TypeUtil.nextPowerOf2(3), Matchers.is(4)); - assertThat(TypeUtil.nextPowerOf2(4), Matchers.is(4)); - - int value = 4; - while (value < Integer.MAX_VALUE / 2) - { - assertThat(TypeUtil.nextPowerOf2(value - 1), Matchers.is(value)); - assertThat(TypeUtil.nextPowerOf2(value), Matchers.is(value)); - assertThat(TypeUtil.nextPowerOf2(value + 1), Matchers.is(value * 2)); - value = value * 2; - } - - assertThat(TypeUtil.nextPowerOf2(Integer.MAX_VALUE / 2), Matchers.is(0x40000000)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2((Integer.MAX_VALUE / 2) + 1)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(Integer.MAX_VALUE)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(-1)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.nextPowerOf2(Integer.MIN_VALUE)); - } - - @Test - public void testLogNextPowerOf2() - { - assertThat(TypeUtil.log2NextPowerOf2(0), Matchers.is(0)); - assertThat(TypeUtil.log2NextPowerOf2(1), Matchers.is(0)); - assertThat(TypeUtil.log2NextPowerOf2(2), Matchers.is(1)); - assertThat(TypeUtil.log2NextPowerOf2(3), Matchers.is(2)); - assertThat(TypeUtil.log2NextPowerOf2(4), Matchers.is(2)); - - int value = 4; - int power = 2; - while (value < Integer.MAX_VALUE / 2) - { - System.err.printf("v=%d p=%d%n", value, power); - assertThat(TypeUtil.log2NextPowerOf2(value - 1), Matchers.is(power)); - assertThat(TypeUtil.log2NextPowerOf2(value), Matchers.is(power)); - assertThat(TypeUtil.log2NextPowerOf2(value + 1), Matchers.is(power + 1)); - value = value * 2; - power = power + 1; - } - - assertThat(TypeUtil.log2NextPowerOf2(Integer.MAX_VALUE / 2), Matchers.is(30)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2((Integer.MAX_VALUE / 2) + 1)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(Integer.MAX_VALUE)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(-1)); - assertThrows(IllegalArgumentException.class, () -> TypeUtil.log2NextPowerOf2(Integer.MIN_VALUE)); - } } From 15c825fea13cead42c29d33ea59459372a85a04d Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 20 Jul 2021 12:09:53 +1000 Subject: [PATCH 72/75] Merge remote-tracking branch 'origin/jetty-10.0.x-6322-UseRetainableByteBuffer' into jetty-10.0.x-6322-UseRetainableByteBuffer-LogBuckets Updates from review Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 64 +++++++++---------- .../jetty/io/RetainableByteBufferPool.java | 57 ----------------- .../io/ArrayRetainableByteBufferPoolTest.java | 47 ++++++++++++-- 3 files changed, 73 insertions(+), 95 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 2f812aa3e708..88cf4521d927 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -78,9 +78,7 @@ protected ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapa bucketCapacity = i -> (i + 1) * f; int length = bucketIndexFor.apply(maxCapacity) + 1; - @SuppressWarnings("unchecked") Bucket[] directArray = new Bucket[length]; - @SuppressWarnings("unchecked") Bucket[] indirectArray = new Bucket[length]; for (int i = 0; i < directArray.length; i++) { @@ -372,37 +370,39 @@ private static class Bucket extends Pool @Override public String toString() { - return String.format("%s{capacity=%d}", super.toString(), _capacity); - } - } - - /** - * A variant of the {@link ArrayRetainableByteBufferPool} that - * uses buckets of buffers that increase in size by a power of - * 2 (eg 1k, 2k, 4k, 8k, etc.). - */ - public static class ExponentialPool extends ArrayRetainableByteBufferPool - { - public ExponentialPool() - { - this(0, -1, Integer.MAX_VALUE); - } - - public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize) - { - this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); - } + int entries = 0; + int inUse = 0; + long used = 0; + long capacity = 0; + for (Entry entry : values()) + { + entries++; + if (entry.isInUse()) + { + inUse++; + + // Looking at the buffer indexes is intrinsically a race. There will be some bad samples. + ByteBuffer buffer = entry.getPooled().getBuffer(); + int pos = buffer.position(); + int lim = buffer.limit(); + + // We can't tell the difference between empty and totally full buffers due to fill/flush mode + // So we only sample usage when either pos or lim is not maximized + if (pos > 0 || lim < buffer.capacity()) + { + // If limit is not capacity, then flush mode, otherwise fill mode + used += (lim < buffer.capacity()) ? lim : pos; + capacity += buffer.capacity(); + } + } + } - public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) - { - super(minCapacity, - -1, - maxCapacity, - maxBucketSize, - maxHeapMemory, - maxDirectMemory, - c -> 32 - Integer.numberOfLeadingZeros(c - 1), - i -> 1 << i); + return String.format("%s{capacity=%d,inuse=%d(%d%%),used=%d%%}", + super.toString(), + _capacity, + inUse, + entries > 0 ? (inUse * 100) / entries : 0, + capacity > 0 ? (used * 100) / capacity : 0); } } } diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java index 76595c161a6c..a4bd9d3754c2 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/RetainableByteBufferPool.java @@ -13,14 +13,9 @@ package org.eclipse.jetty.io; -import java.io.IOException; import java.nio.ByteBuffer; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Container; -import org.eclipse.jetty.util.component.Dumpable; -import org.eclipse.jetty.util.statistic.SampleStatistic; /** *

    A {@link RetainableByteBuffer} pool.

    @@ -60,56 +55,4 @@ static RetainableByteBufferPool findOrAdapt(Container container, ByteBufferPool } return retainableByteBufferPool; } - - /** - * A Pool wrapper that collects sample statistics on the acquired - * buffers. - * @see SampleStatistic - */ - @ManagedObject - class SampledPool implements RetainableByteBufferPool, Dumpable - { - private final RetainableByteBufferPool _pool; - private final SampleStatistic _heap = new SampleStatistic(); - private final SampleStatistic _direct = new SampleStatistic(); - - public SampledPool(RetainableByteBufferPool pool) - { - _pool = pool; - } - - @ManagedAttribute("Heap acquire samples") - public SampleStatistic getHeapSample() - { - return _heap; - } - - @ManagedAttribute("Direct acquire samples") - public SampleStatistic getDirectSample() - { - return _direct; - } - - @Override - public RetainableByteBuffer acquire(int size, boolean direct) - { - (direct ? _direct : _heap).record(size); - return _pool.acquire(size, direct); - } - - @Override - public void dump(Appendable out, String indent) throws IOException - { - Dumpable.dumpObjects(out, indent, this, - Dumpable.named("heap", _heap), - Dumpable.named("direct", _direct), - _pool); - } - - @ManagedAttribute("Pool") - public RetainableByteBufferPool getPool() - { - return _pool; - } - } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index 87a69649839d..129a6f10bbf8 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.lessThanOrEqualTo; @@ -317,13 +318,15 @@ public void testAcquireRelease() } @Test - public void testSampledExponentialPool() throws IOException + public void testExponentialPool() throws IOException { - RetainableByteBufferPool.SampledPool pool = new RetainableByteBufferPool.SampledPool(new ArrayRetainableByteBufferPool.ExponentialPool()); + ArrayRetainableByteBufferPool pool = new ExponentialPool(); assertThat(pool.acquire(1, false).capacity(), is(1)); assertThat(pool.acquire(2, false).capacity(), is(2)); - assertThat(pool.acquire(3, false).capacity(), is(4)); - assertThat(pool.acquire(4, false).capacity(), is(4)); + RetainableByteBuffer b3 = pool.acquire(3, false); + assertThat(b3.capacity(), is(4)); + RetainableByteBuffer b4 = pool.acquire(4, false); + assertThat(b4.capacity(), is(4)); int capacity = 4; while (true) @@ -333,7 +336,7 @@ public void testSampledExponentialPool() throws IOException b = pool.acquire(capacity, false); assertThat(b.capacity(), Matchers.is(capacity)); - if (capacity >= ((ArrayRetainableByteBufferPool)pool.getPool()).getMaxCapacity()) + if (capacity >= pool.getMaxCapacity()) break; b = pool.acquire(capacity + 1, false); @@ -342,6 +345,38 @@ public void testSampledExponentialPool() throws IOException capacity = capacity * 2; } - System.err.println(pool.dump()); + b3.release(); + b4.getBuffer().limit(b4.getBuffer().capacity()-2); + assertThat(pool.dump(), containsString("[size=4 closed=false]{capacity=4,inuse=3(75%),used=16%")); + } + + /** + * A variant of the {@link ArrayRetainableByteBufferPool} that + * uses buckets of buffers that increase in size by a power of + * 2 (eg 1k, 2k, 4k, 8k, etc.). + */ + public static class ExponentialPool extends ArrayRetainableByteBufferPool + { + public ExponentialPool() + { + this(0, -1, Integer.MAX_VALUE); + } + + public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize) + { + this(minCapacity, maxCapacity, maxBucketSize, -1L, -1L); + } + + public ExponentialPool(int minCapacity, int maxCapacity, int maxBucketSize, long maxHeapMemory, long maxDirectMemory) + { + super(minCapacity, + -1, + maxCapacity, + maxBucketSize, + maxHeapMemory, + maxDirectMemory, + c -> 32 - Integer.numberOfLeadingZeros(c - 1), + i -> 1 << i); + } } } From b70b31508593c55582302ad9f335a44582b6b6fb Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 20 Jul 2021 12:16:08 +1000 Subject: [PATCH 73/75] Merge remote-tracking branch 'origin/jetty-10.0.x-6322-UseRetainableByteBuffer' into jetty-10.0.x-6322-UseRetainableByteBuffer-LogBuckets Updates from review Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 88cf4521d927..4ab889ba8169 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -68,7 +68,7 @@ protected ArrayRetainableByteBufferPool(int minCapacity, int factor, int maxCapa if (maxCapacity <= 0) maxCapacity = 64 * 1024; - final int f = factor <= 0 ? 1024 : factor; + int f = factor <= 0 ? 1024 : factor; if ((maxCapacity % f) != 0 || f >= maxCapacity) throw new IllegalArgumentException("The capacity factor must be a divisor of maxCapacity"); From 5e2190321b22a30d96efbbb2b1c181bbe7ca4910 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 22 Jul 2021 08:54:57 +1000 Subject: [PATCH 74/75] Merge remote-tracking branch 'origin/jetty-10.0.x-6322-UseRetainableByteBuffer' into jetty-10.0.x-6322-UseRetainableByteBuffer-LogBuckets fix checkstyle Signed-off-by: Greg Wilkins --- .../org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index 129a6f10bbf8..9b7a3714052b 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -346,7 +346,7 @@ public void testExponentialPool() throws IOException } b3.release(); - b4.getBuffer().limit(b4.getBuffer().capacity()-2); + b4.getBuffer().limit(b4.getBuffer().capacity() - 2); assertThat(pool.dump(), containsString("[size=4 closed=false]{capacity=4,inuse=3(75%),used=16%")); } From 9b10d54d19bf27b6184a6c95cc3e30d59f2d2de3 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 22 Jul 2021 08:57:36 +1000 Subject: [PATCH 75/75] Merge remote-tracking branch 'origin/jetty-10.0.x-6322-UseRetainableByteBuffer' into jetty-10.0.x-6322-UseRetainableByteBuffer-LogBuckets fix checkstyle removed useless metric Signed-off-by: Greg Wilkins --- .../io/ArrayRetainableByteBufferPool.java | 23 ++----------------- .../io/ArrayRetainableByteBufferPoolTest.java | 2 +- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java index 4ab889ba8169..a33159a49489 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPool.java @@ -372,37 +372,18 @@ public String toString() { int entries = 0; int inUse = 0; - long used = 0; - long capacity = 0; for (Entry entry : values()) { entries++; if (entry.isInUse()) - { inUse++; - - // Looking at the buffer indexes is intrinsically a race. There will be some bad samples. - ByteBuffer buffer = entry.getPooled().getBuffer(); - int pos = buffer.position(); - int lim = buffer.limit(); - - // We can't tell the difference between empty and totally full buffers due to fill/flush mode - // So we only sample usage when either pos or lim is not maximized - if (pos > 0 || lim < buffer.capacity()) - { - // If limit is not capacity, then flush mode, otherwise fill mode - used += (lim < buffer.capacity()) ? lim : pos; - capacity += buffer.capacity(); - } - } } - return String.format("%s{capacity=%d,inuse=%d(%d%%),used=%d%%}", + return String.format("%s{capacity=%d,inuse=%d(%d%%)}", super.toString(), _capacity, inUse, - entries > 0 ? (inUse * 100) / entries : 0, - capacity > 0 ? (used * 100) / capacity : 0); + entries > 0 ? (inUse * 100) / entries : 0); } } } diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java index 9b7a3714052b..f0b7201588d4 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/ArrayRetainableByteBufferPoolTest.java @@ -347,7 +347,7 @@ public void testExponentialPool() throws IOException b3.release(); b4.getBuffer().limit(b4.getBuffer().capacity() - 2); - assertThat(pool.dump(), containsString("[size=4 closed=false]{capacity=4,inuse=3(75%),used=16%")); + assertThat(pool.dump(), containsString("[size=4 closed=false]{capacity=4,inuse=3(75%)")); } /**