View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.util.UncheckedBooleanSupplier;
21  
22  import java.util.AbstractMap;
23  import java.util.Map.Entry;
24  
25  /**
26   * The {@link RecvByteBufAllocator} that yields a buffer size prediction based upon decrementing the value from
27   * the max bytes per read.
28   */
29  public class DefaultMaxBytesRecvByteBufAllocator implements MaxBytesRecvByteBufAllocator {
30      private volatile int maxBytesPerRead;
31      private volatile int maxBytesPerIndividualRead;
32  
33      private final class HandleImpl implements ExtendedHandle {
34          private int individualReadMax;
35          private int bytesToRead;
36          private int lastBytesRead;
37          private int attemptBytesRead;
38          private final UncheckedBooleanSupplier defaultMaybeMoreSupplier = new UncheckedBooleanSupplier() {
39              @Override
40              public boolean get() {
41                  return attemptBytesRead == lastBytesRead;
42              }
43          };
44  
45          @Override
46          public ByteBuf allocate(ByteBufAllocator alloc) {
47              return alloc.ioBuffer(guess());
48          }
49  
50          @Override
51          public int guess() {
52              return Math.min(individualReadMax, bytesToRead);
53          }
54  
55          @Override
56          public void reset(ChannelConfig config) {
57              bytesToRead = maxBytesPerRead();
58              individualReadMax = maxBytesPerIndividualRead();
59          }
60  
61          @Override
62          public void incMessagesRead(int amt) {
63          }
64  
65          @Override
66          public void lastBytesRead(int bytes) {
67              lastBytesRead = bytes;
68              // Ignore if bytes is negative, the interface contract states it will be detected externally after call.
69              // The value may be "invalid" after this point, but it doesn't matter because reading will be stopped.
70              bytesToRead -= bytes;
71          }
72  
73          @Override
74          public int lastBytesRead() {
75              return lastBytesRead;
76          }
77  
78          @Override
79          public boolean continueReading() {
80              return continueReading(defaultMaybeMoreSupplier);
81          }
82  
83          @Override
84          public boolean continueReading(UncheckedBooleanSupplier maybeMoreDataSupplier) {
85              // Keep reading if we are allowed to read more bytes, and our last read filled up the buffer we provided.
86              return bytesToRead > 0 && maybeMoreDataSupplier.get();
87          }
88  
89          @Override
90          public void readComplete() {
91          }
92  
93          @Override
94          public void attemptedBytesRead(int bytes) {
95              attemptBytesRead = bytes;
96          }
97  
98          @Override
99          public int attemptedBytesRead() {
100             return attemptBytesRead;
101         }
102     }
103 
104     public DefaultMaxBytesRecvByteBufAllocator() {
105         this(64 * 1024, 64 * 1024);
106     }
107 
108     public DefaultMaxBytesRecvByteBufAllocator(int maxBytesPerRead, int maxBytesPerIndividualRead) {
109         checkMaxBytesPerReadPair(maxBytesPerRead, maxBytesPerIndividualRead);
110         this.maxBytesPerRead = maxBytesPerRead;
111         this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
112     }
113 
114     @SuppressWarnings("deprecation")
115     @Override
116     public Handle newHandle() {
117         return new HandleImpl();
118     }
119 
120     @Override
121     public int maxBytesPerRead() {
122         return maxBytesPerRead;
123     }
124 
125     @Override
126     public DefaultMaxBytesRecvByteBufAllocator maxBytesPerRead(int maxBytesPerRead) {
127         if (maxBytesPerRead <= 0) {
128             throw new IllegalArgumentException("maxBytesPerRead: " + maxBytesPerRead + " (expected: > 0)");
129         }
130         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
131         // Write operations must be synchronized, but independent read operations can just be volatile.
132         synchronized (this) {
133             final int maxBytesPerIndividualRead = maxBytesPerIndividualRead();
134             if (maxBytesPerRead < maxBytesPerIndividualRead) {
135                 throw new IllegalArgumentException(
136                         "maxBytesPerRead cannot be less than " +
137                                 "maxBytesPerIndividualRead (" + maxBytesPerIndividualRead + "): " + maxBytesPerRead);
138             }
139 
140             this.maxBytesPerRead = maxBytesPerRead;
141         }
142         return this;
143     }
144 
145     @Override
146     public int maxBytesPerIndividualRead() {
147         return maxBytesPerIndividualRead;
148     }
149 
150     @Override
151     public DefaultMaxBytesRecvByteBufAllocator maxBytesPerIndividualRead(int maxBytesPerIndividualRead) {
152         if (maxBytesPerIndividualRead <= 0) {
153             throw new IllegalArgumentException(
154                     "maxBytesPerIndividualRead: " + maxBytesPerIndividualRead + " (expected: > 0)");
155         }
156         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
157         // Write operations must be synchronized, but independent read operations can just be volatile.
158         synchronized (this) {
159             final int maxBytesPerRead = maxBytesPerRead();
160             if (maxBytesPerIndividualRead > maxBytesPerRead) {
161                 throw new IllegalArgumentException(
162                         "maxBytesPerIndividualRead cannot be greater than " +
163                                 "maxBytesPerRead (" + maxBytesPerRead + "): " + maxBytesPerIndividualRead);
164             }
165 
166             this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
167         }
168         return this;
169     }
170 
171     @Override
172     public synchronized Entry<Integer, Integer> maxBytesPerReadPair() {
173         return new AbstractMap.SimpleEntry<Integer, Integer>(maxBytesPerRead, maxBytesPerIndividualRead);
174     }
175 
176     private static void checkMaxBytesPerReadPair(int maxBytesPerRead, int maxBytesPerIndividualRead) {
177         if (maxBytesPerRead <= 0) {
178             throw new IllegalArgumentException("maxBytesPerRead: " + maxBytesPerRead + " (expected: > 0)");
179         }
180         if (maxBytesPerIndividualRead <= 0) {
181             throw new IllegalArgumentException(
182                     "maxBytesPerIndividualRead: " + maxBytesPerIndividualRead + " (expected: > 0)");
183         }
184         if (maxBytesPerRead < maxBytesPerIndividualRead) {
185             throw new IllegalArgumentException(
186                     "maxBytesPerRead cannot be less than " +
187                             "maxBytesPerIndividualRead (" + maxBytesPerIndividualRead + "): " + maxBytesPerRead);
188         }
189     }
190 
191     @Override
192     public DefaultMaxBytesRecvByteBufAllocator maxBytesPerReadPair(int maxBytesPerRead,
193             int maxBytesPerIndividualRead) {
194         checkMaxBytesPerReadPair(maxBytesPerRead, maxBytesPerIndividualRead);
195         // There is a dependency between this.maxBytesPerRead and this.maxBytesPerIndividualRead (a < b).
196         // Write operations must be synchronized, but independent read operations can just be volatile.
197         synchronized (this) {
198             this.maxBytesPerRead = maxBytesPerRead;
199             this.maxBytesPerIndividualRead = maxBytesPerIndividualRead;
200         }
201         return this;
202     }
203 }