1717package io .netty .buffer ;
1818
1919import io .netty .util .internal .CleanableDirectBuffer ;
20+ import io .netty .util .internal .ObjectPool ;
2021import io .netty .util .internal .PlatformDependent ;
2122import io .netty .util .internal .StringUtil ;
22-
2323import java .nio .ByteBuffer ;
2424import java .util .ArrayList ;
2525import java .util .Collections ;
2626import java .util .List ;
27+ import java .util .Queue ;
2728import java .util .concurrent .atomic .AtomicInteger ;
2829import java .util .concurrent .atomic .AtomicReference ;
2930import java .util .concurrent .atomic .LongAdder ;
3031import java .util .concurrent .locks .ReentrantLock ;
31-
3232import static io .netty .buffer .PoolChunk .isSubpage ;
33+ import static io .netty .buffer .PooledByteBufAllocator .ARENA_BUFFER_QUEUE_CAPACITY_FOR_NON_THREAD_LOCAL ;
3334import static java .lang .Math .max ;
3435
3536abstract class PoolArena <T > implements PoolArenaMetric {
@@ -127,7 +128,7 @@ private PoolSubpage<T>[] newSubpagePoolArray(int size) {
127128 abstract boolean isDirect ();
128129
129130 PooledByteBuf <T > allocate (PoolThreadCache cache , int reqCapacity , int maxCapacity ) {
130- PooledByteBuf <T > buf = newByteBuf (maxCapacity );
131+ PooledByteBuf <T > buf = newByteBuf (maxCapacity , cache );
131132 allocate (cache , buf , reqCapacity );
132133 return buf ;
133134 }
@@ -150,7 +151,7 @@ private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int req
150151 private void tcacheAllocateSmall (PoolThreadCache cache , PooledByteBuf <T > buf , final int reqCapacity ,
151152 final int sizeIdx ) {
152153
153- if (cache .allocateSmall (this , buf , reqCapacity , sizeIdx )) {
154+ if (cache .useThreadLocal () && cache . allocateSmall (this , buf , reqCapacity , sizeIdx )) {
154155 // was able to allocate out of the cache so move on
155156 return ;
156157 }
@@ -190,7 +191,7 @@ private void tcacheAllocateSmall(PoolThreadCache cache, PooledByteBuf<T> buf, fi
190191
191192 private void tcacheAllocateNormal (PoolThreadCache cache , PooledByteBuf <T > buf , final int reqCapacity ,
192193 final int sizeIdx ) {
193- if (cache .allocateNormal (this , buf , reqCapacity , sizeIdx )) {
194+ if (cache .useThreadLocal () && cache . allocateNormal (this , buf , reqCapacity , sizeIdx )) {
194195 // was able to allocate out of the cache so move on
195196 return ;
196197 }
@@ -243,7 +244,8 @@ void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacit
243244 deallocationsHuge .increment ();
244245 } else {
245246 SizeClass sizeClass = sizeClass (handle );
246- if (cache != null && cache .add (this , chunk , nioBuffer , handle , normCapacity , sizeClass )) {
247+ if (cache != null && cache .useThreadLocal () &&
248+ cache .add (this , chunk , nioBuffer , handle , normCapacity , sizeClass )) {
247249 // cached so not free it.
248250 return ;
249251 }
@@ -574,7 +576,7 @@ public long numPinnedBytes() {
574576
575577 protected abstract PoolChunk <T > newChunk (int pageSize , int maxPageIdx , int pageShifts , int chunkSize );
576578 protected abstract PoolChunk <T > newUnpooledChunk (int capacity );
577- protected abstract PooledByteBuf <T > newByteBuf (int maxCapacity );
579+ protected abstract PooledByteBuf <T > newByteBuf (int maxCapacity , PoolThreadCache cache );
578580 protected abstract void memoryCopy (T src , int srcOffset , PooledByteBuf <T > dst , int length );
579581 protected abstract void destroyChunk (PoolChunk <T > chunk );
580582
@@ -661,10 +663,34 @@ private void destroyPoolChunkLists(PoolChunkList<T>... chunkLists) {
661663
662664 static final class HeapArena extends PoolArena <byte []> {
663665 private final AtomicReference <PoolChunk <byte []>> lastDestroyedChunk ;
666+ private final Queue <PooledByteBuf <byte []>> bufferQueue ;
667+ private final ObjectPool .Handle <PooledHeapByteBuf > handle ;
664668
665669 HeapArena (PooledByteBufAllocator parent , SizeClasses sizeClass ) {
670+ this (parent , sizeClass , true );
671+ }
672+
673+ HeapArena (PooledByteBufAllocator parent , SizeClasses sizeClass , boolean useThreadLocal ) {
666674 super (parent , sizeClass );
667675 lastDestroyedChunk = new AtomicReference <>();
676+ if (useThreadLocal ) {
677+ bufferQueue = null ;
678+ handle = new ObjectPool .Handle <PooledHeapByteBuf >() {
679+ @ Override
680+ public void recycle (PooledHeapByteBuf self ) {
681+ // noop
682+ }
683+ };
684+ } else {
685+ int qSize = Math .max (ARENA_BUFFER_QUEUE_CAPACITY_FOR_NON_THREAD_LOCAL , 2 );
686+ bufferQueue = PlatformDependent .newFixedMpmcQueue (qSize );
687+ handle = new ObjectPool .Handle <PooledHeapByteBuf >() {
688+ @ Override
689+ public void recycle (PooledHeapByteBuf self ) {
690+ bufferQueue .offer (self );
691+ }
692+ };
693+ }
668694 }
669695
670696 private static byte [] newByteArray (int size ) {
@@ -705,9 +731,18 @@ protected void destroyChunk(PoolChunk<byte[]> chunk) {
705731 }
706732
707733 @ Override
708- protected PooledByteBuf <byte []> newByteBuf (int maxCapacity ) {
709- return HAS_UNSAFE ? PooledUnsafeHeapByteBuf .newUnsafeInstance (maxCapacity )
710- : PooledHeapByteBuf .newInstance (maxCapacity );
734+ protected PooledByteBuf <byte []> newByteBuf (int maxCapacity , PoolThreadCache threadCache ) {
735+ if (threadCache .useThreadLocal ()) {
736+ return HAS_UNSAFE ? PooledUnsafeHeapByteBuf .newUnsafeInstance (maxCapacity )
737+ : PooledHeapByteBuf .newInstance (maxCapacity );
738+ }
739+ PooledByteBuf <byte []> buf = bufferQueue == null ? null : bufferQueue .poll ();
740+ if (buf == null ) {
741+ buf = HAS_UNSAFE ? PooledUnsafeHeapByteBuf .newInstanceNoThreadLocal (handle )
742+ : PooledHeapByteBuf .newInstanceNoThreadLocal (handle );
743+ }
744+ buf .reuse (maxCapacity );
745+ return buf ;
711746 }
712747
713748 @ Override
@@ -721,9 +756,33 @@ protected void memoryCopy(byte[] src, int srcOffset, PooledByteBuf<byte[]> dst,
721756 }
722757
723758 static final class DirectArena extends PoolArena <ByteBuffer > {
759+ private final Queue <PooledByteBuf <ByteBuffer >> bufferQueue ;
760+ private final ObjectPool .Handle <PooledByteBuf <ByteBuffer >> handle ;
724761
725762 DirectArena (PooledByteBufAllocator parent , SizeClasses sizeClass ) {
763+ this (parent , sizeClass , true );
764+ }
765+
766+ DirectArena (PooledByteBufAllocator parent , SizeClasses sizeClass , boolean useThreadLocal ) {
726767 super (parent , sizeClass );
768+ if (useThreadLocal ) {
769+ bufferQueue = null ;
770+ handle = new ObjectPool .Handle <PooledByteBuf <ByteBuffer >>() {
771+ @ Override
772+ public void recycle (PooledByteBuf <ByteBuffer > self ) {
773+ // noop
774+ }
775+ };
776+ } else {
777+ int qSize = Math .max (ARENA_BUFFER_QUEUE_CAPACITY_FOR_NON_THREAD_LOCAL , 2 );
778+ bufferQueue = PlatformDependent .newFixedMpmcQueue (qSize );
779+ handle = new ObjectPool .Handle <PooledByteBuf <ByteBuffer >>() {
780+ @ Override
781+ public void recycle (PooledByteBuf <ByteBuffer > self ) {
782+ bufferQueue .offer (self );
783+ }
784+ };
785+ }
727786 }
728787
729788 @ Override
@@ -774,12 +833,19 @@ protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
774833 }
775834
776835 @ Override
777- protected PooledByteBuf <ByteBuffer > newByteBuf (int maxCapacity ) {
778- if (HAS_UNSAFE ) {
779- return PooledUnsafeDirectByteBuf .newInstance (maxCapacity );
780- } else {
781- return PooledDirectByteBuf .newInstance (maxCapacity );
836+ protected PooledByteBuf <ByteBuffer > newByteBuf (int maxCapacity , PoolThreadCache threadCache ) {
837+ if (threadCache .useThreadLocal ()) {
838+ return HAS_UNSAFE ? PooledUnsafeDirectByteBuf .newInstance (maxCapacity )
839+ : PooledDirectByteBuf .newInstance (maxCapacity );
840+ }
841+ PooledByteBuf <ByteBuffer > buf = bufferQueue == null ? null : bufferQueue .poll ();
842+ if (buf == null ) {
843+ buf = HAS_UNSAFE ?
844+ PooledUnsafeDirectByteBuf .newInstanceNoThreadLocal (handle )
845+ : PooledDirectByteBuf .newInstanceNoThreadLocal (handle );
782846 }
847+ buf .reuse (maxCapacity );
848+ return buf ;
783849 }
784850
785851 @ Override
0 commit comments