1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel;
17
18 import io.netty.util.concurrent.Future;
19 import io.netty.util.concurrent.Promise;
20 import io.netty.util.concurrent.RejectedExecutionHandler;
21 import io.netty.util.concurrent.SingleThreadEventExecutor;
22 import io.netty.util.internal.ObjectUtil;
23 import io.netty.util.internal.PlatformDependent;
24 import io.netty.util.internal.SystemPropertyUtil;
25
26 import java.util.Queue;
27 import java.util.concurrent.Executor;
28 import java.util.concurrent.ThreadFactory;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.atomic.AtomicInteger;
31
32
33
34
35
36 public class SingleThreadIoEventLoop extends SingleThreadEventLoop implements IoEventLoop {
37
38
39 private static final long DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS = TimeUnit.MILLISECONDS.toNanos(Math.max(100,
40 SystemPropertyUtil.getInt("io.netty.eventLoop.maxTaskProcessingQuantumMs", 1000)));
41
42
43 static final int DEFAULT_IO_RATIO = Math.max(1, Math.min(100,
44 SystemPropertyUtil.getInt("io.netty.eventLoop.ioRatio", 100)));
45
46 private final long maxTaskProcessingQuantumNs;
47 private volatile int ioRatio = DEFAULT_IO_RATIO;
48 private long activeIoTimeNanos = -1;
49 private final IoHandlerContext context = new IoHandlerContext() {
50 @Override
51 public boolean canBlock() {
52 assert inEventLoop();
53 return !hasTasks() && !hasScheduledTasks();
54 }
55
56 @Override
57 public long delayNanos(long currentTimeNanos) {
58 assert inEventLoop();
59 return SingleThreadIoEventLoop.this.delayNanos(currentTimeNanos);
60 }
61
62 @Override
63 public long deadlineNanos() {
64 assert inEventLoop();
65 return SingleThreadIoEventLoop.this.deadlineNanos();
66 }
67
68 @Override
69 public void reportActiveIoTime(long activeNanos) {
70 if (activeNanos >= 0) {
71 activeIoTimeNanos = activeNanos;
72 }
73 SingleThreadIoEventLoop.this.reportActiveIoTime(activeNanos);
74 }
75
76 @Override
77 public boolean shouldReportActiveIoTime() {
78 return isSuspensionSupported() || ioRatio != 100;
79 }
80 };
81
82 private final IoHandler ioHandler;
83
84 private final AtomicInteger numRegistrations = new AtomicInteger();
85
86
87
88
89
90
91
92
93
94 public SingleThreadIoEventLoop(IoEventLoopGroup parent, ThreadFactory threadFactory,
95 IoHandlerFactory ioHandlerFactory) {
96 super(parent, threadFactory, false,
97 ObjectUtil.checkNotNull(ioHandlerFactory, "ioHandlerFactory").isChangingThreadSupported());
98 this.maxTaskProcessingQuantumNs = DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS;
99 this.ioHandler = ioHandlerFactory.newHandler(this);
100 }
101
102
103
104
105
106
107
108
109
110 public SingleThreadIoEventLoop(IoEventLoopGroup parent, Executor executor, IoHandlerFactory ioHandlerFactory) {
111 super(parent, executor, false,
112 ObjectUtil.checkNotNull(ioHandlerFactory, "ioHandlerFactory").isChangingThreadSupported());
113 this.maxTaskProcessingQuantumNs = DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS;
114 this.ioHandler = ioHandlerFactory.newHandler(this);
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 public SingleThreadIoEventLoop(IoEventLoopGroup parent, ThreadFactory threadFactory,
135 IoHandlerFactory ioHandlerFactory, int maxPendingTasks,
136 RejectedExecutionHandler rejectedExecutionHandler, long maxTaskProcessingQuantumMs) {
137 super(parent, threadFactory, false,
138 ObjectUtil.checkNotNull(ioHandlerFactory, "ioHandlerFactory").isChangingThreadSupported(),
139 maxPendingTasks, rejectedExecutionHandler);
140 this.maxTaskProcessingQuantumNs =
141 ObjectUtil.checkPositiveOrZero(maxTaskProcessingQuantumMs, "maxTaskProcessingQuantumMs") == 0 ?
142 DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS :
143 TimeUnit.MILLISECONDS.toNanos(maxTaskProcessingQuantumMs);
144 this.ioHandler = ioHandlerFactory.newHandler(this);
145 }
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 public SingleThreadIoEventLoop(IoEventLoopGroup parent, Executor executor,
163 IoHandlerFactory ioHandlerFactory, int maxPendingTasks,
164 RejectedExecutionHandler rejectedExecutionHandler,
165 long maxTaskProcessingQuantumMs) {
166 super(parent, executor, false,
167 ObjectUtil.checkNotNull(ioHandlerFactory, "ioHandlerFactory").isChangingThreadSupported(),
168 maxPendingTasks, rejectedExecutionHandler);
169 this.maxTaskProcessingQuantumNs =
170 ObjectUtil.checkPositiveOrZero(maxTaskProcessingQuantumMs, "maxTaskProcessingQuantumMs") == 0 ?
171 DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS :
172 TimeUnit.MILLISECONDS.toNanos(maxTaskProcessingQuantumMs);
173 this.ioHandler = ioHandlerFactory.newHandler(this);
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 protected SingleThreadIoEventLoop(IoEventLoopGroup parent, Executor executor,
190 IoHandlerFactory ioHandlerFactory, Queue<Runnable> taskQueue,
191 Queue<Runnable> tailTaskQueue,
192 RejectedExecutionHandler rejectedExecutionHandler) {
193 super(parent, executor, false,
194 ObjectUtil.checkNotNull(ioHandlerFactory, "ioHandlerFactory").isChangingThreadSupported(),
195 taskQueue, tailTaskQueue, rejectedExecutionHandler);
196 this.maxTaskProcessingQuantumNs = DEFAULT_MAX_TASK_PROCESSING_QUANTUM_NS;
197 this.ioHandler = ioHandlerFactory.newHandler(this);
198 }
199
200 @Override
201 protected void run() {
202 assert inEventLoop();
203 ioHandler.initialize();
204 do {
205 final int ioRatio = this.ioRatio;
206 final long taskQuantumNs;
207
208
209 activeIoTimeNanos = -1;
210 runIo();
211 if (ioRatio == 100 || activeIoTimeNanos == -1) {
212 taskQuantumNs = maxTaskProcessingQuantumNs;
213 } else {
214
215
216 long quantum = Math.min(maxTaskProcessingQuantumNs, activeIoTimeNanos * (100 - ioRatio) / ioRatio);
217 if (quantum == 0) {
218
219 taskQuantumNs = maxTaskProcessingQuantumNs;
220 } else {
221 taskQuantumNs = quantum;
222 }
223 }
224 if (isShuttingDown()) {
225 ioHandler.prepareToDestroy();
226 }
227
228 runAllTasks(taskQuantumNs);
229
230
231 } while (!confirmShutdown() && !canSuspend());
232 }
233
234 protected final IoHandler ioHandler() {
235 return ioHandler;
236 }
237
238
239
240
241
242
243
244 public int getIoRatio() {
245 return ioRatio;
246 }
247
248
249
250
251
252
253
254
255
256 public void setIoRatio(int ioRatio) {
257 this.ioRatio = ObjectUtil.checkInRange(ioRatio, 1, 100, "ioRatio");
258 }
259
260 @Override
261 protected boolean canSuspend(int state) {
262
263 return super.canSuspend(state) && numRegistrations.get() == 0;
264 }
265
266
267
268
269
270
271
272 protected int runIo() {
273 assert inEventLoop();
274 return ioHandler.run(context);
275 }
276
277 @Override
278 public IoEventLoop next() {
279 return this;
280 }
281
282 @Override
283 public final Future<IoRegistration> register(final IoHandle handle) {
284 Promise<IoRegistration> promise = newPromise();
285 if (inEventLoop()) {
286 registerForIo0(handle, promise);
287 } else {
288 execute(() -> registerForIo0(handle, promise));
289 }
290
291 return promise;
292 }
293
294 @Override
295 protected int getNumOfRegisteredChannels() {
296 return numRegistrations.get();
297 }
298
299 private void registerForIo0(final IoHandle handle, Promise<IoRegistration> promise) {
300 assert inEventLoop();
301 final IoRegistration registration;
302 try {
303 registration = ioHandler.register(handle);
304 } catch (Exception e) {
305 promise.setFailure(e);
306 return;
307 }
308 numRegistrations.incrementAndGet();
309 promise.setSuccess(new IoRegistrationWrapper(registration));
310 }
311
312 @Override
313 protected final void wakeup(boolean inEventLoop) {
314 ioHandler.wakeup();
315 }
316
317 @Override
318 protected final void cleanup() {
319 assert inEventLoop();
320 ioHandler.destroy();
321 }
322
323 @Override
324 public boolean isCompatible(Class<? extends IoHandle> handleType) {
325 return ioHandler.isCompatible(handleType);
326 }
327
328 @Override
329 public boolean isIoType(Class<? extends IoHandler> handlerType) {
330 return ioHandler.getClass().equals(handlerType);
331 }
332
333 @Override
334 protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
335 return newTaskQueue0(maxPendingTasks);
336 }
337
338 protected static Queue<Runnable> newTaskQueue0(int maxPendingTasks) {
339
340 return maxPendingTasks == Integer.MAX_VALUE ? PlatformDependent.<Runnable>newMpscQueue()
341 : PlatformDependent.<Runnable>newMpscQueue(maxPendingTasks);
342 }
343
344 private final class IoRegistrationWrapper implements IoRegistration {
345 private final IoRegistration registration;
346 IoRegistrationWrapper(IoRegistration registration) {
347 this.registration = registration;
348 }
349
350 @Override
351 public <T> T attachment() {
352 return registration.attachment();
353 }
354
355 @Override
356 public long submit(IoOps ops) {
357 return registration.submit(ops);
358 }
359
360 @Override
361 public boolean isValid() {
362 return registration.isValid();
363 }
364
365 @Override
366 public boolean cancel() {
367 if (registration.cancel()) {
368 numRegistrations.decrementAndGet();
369 return true;
370 }
371 return false;
372 }
373 }
374 }