1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.local;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.AbstractChannel;
20 import io.netty.channel.Channel;
21 import io.netty.channel.ChannelConfig;
22 import io.netty.channel.ChannelMetadata;
23 import io.netty.channel.ChannelOutboundBuffer;
24 import io.netty.channel.ChannelPipeline;
25 import io.netty.channel.ChannelPromise;
26 import io.netty.channel.DefaultChannelConfig;
27 import io.netty.channel.EventLoop;
28 import io.netty.channel.PreferHeapByteBufAllocator;
29 import io.netty.channel.RecvByteBufAllocator;
30 import io.netty.channel.SingleThreadEventLoop;
31 import io.netty.util.ReferenceCountUtil;
32 import io.netty.util.concurrent.Future;
33 import io.netty.util.concurrent.SingleThreadEventExecutor;
34 import io.netty.util.internal.InternalThreadLocalMap;
35 import io.netty.util.internal.PlatformDependent;
36 import io.netty.util.internal.logging.InternalLogger;
37 import io.netty.util.internal.logging.InternalLoggerFactory;
38
39 import java.net.ConnectException;
40 import java.net.SocketAddress;
41 import java.nio.channels.AlreadyConnectedException;
42 import java.nio.channels.ClosedChannelException;
43 import java.nio.channels.ConnectionPendingException;
44 import java.nio.channels.NotYetConnectedException;
45 import java.util.Queue;
46 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
47
48
49
50
51 public class LocalChannel extends AbstractChannel {
52 private static final InternalLogger logger = InternalLoggerFactory.getInstance(LocalChannel.class);
53 @SuppressWarnings({ "rawtypes" })
54 private static final AtomicReferenceFieldUpdater<LocalChannel, Future> FINISH_READ_FUTURE_UPDATER =
55 AtomicReferenceFieldUpdater.newUpdater(LocalChannel.class, Future.class, "finishReadFuture");
56 private static final AtomicReferenceFieldUpdater<LocalChannel, State> STATE_UPDATER =
57 AtomicReferenceFieldUpdater.newUpdater(LocalChannel.class, State.class, "state");
58 private static final ChannelMetadata METADATA = new ChannelMetadata(false);
59 private static final int MAX_READER_STACK_DEPTH = 8;
60
61 private enum State { OPEN, BOUND, CONNECTED, CLOSED }
62
63 private final ChannelConfig config = new DefaultChannelConfig(this);
64
65 final Queue<Object> inboundBuffer = PlatformDependent.newSpscQueue();
66 private final Runnable readTask = new Runnable() {
67 @Override
68 public void run() {
69
70 if (!inboundBuffer.isEmpty()) {
71 readInbound();
72 }
73 }
74 };
75
76 private final Runnable shutdownHook = new Runnable() {
77 @Override
78 public void run() {
79 unsafe().close(unsafe().voidPromise());
80 }
81 };
82
83 private final Runnable finishReadTask = new Runnable() {
84 @Override
85 public void run() {
86 finishPeerRead0(LocalChannel.this);
87 }
88 };
89
90 private volatile State state;
91 private volatile LocalChannel peer;
92 private volatile LocalAddress localAddress;
93 private volatile LocalAddress remoteAddress;
94 private volatile ChannelPromise connectPromise;
95 private volatile boolean readInProgress;
96 private volatile boolean writeInProgress;
97 private volatile Future<?> finishReadFuture;
98
99 public LocalChannel() {
100 super(null);
101 config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
102 }
103
104 protected LocalChannel(LocalServerChannel parent, LocalChannel peer) {
105 super(parent);
106 config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
107 this.peer = peer;
108 localAddress = parent.localAddress();
109 remoteAddress = peer.localAddress();
110 }
111
112 @Override
113 public ChannelMetadata metadata() {
114 return METADATA;
115 }
116
117 @Override
118 public ChannelConfig config() {
119 return config;
120 }
121
122 @Override
123 public LocalServerChannel parent() {
124 return (LocalServerChannel) super.parent();
125 }
126
127 @Override
128 public LocalAddress localAddress() {
129 return (LocalAddress) super.localAddress();
130 }
131
132 @Override
133 public LocalAddress remoteAddress() {
134 return (LocalAddress) super.remoteAddress();
135 }
136
137 @Override
138 public boolean isOpen() {
139 return state != State.CLOSED;
140 }
141
142 @Override
143 public boolean isActive() {
144 return state == State.CONNECTED;
145 }
146
147 @Override
148 protected AbstractUnsafe newUnsafe() {
149 return new LocalUnsafe();
150 }
151
152 @Override
153 protected boolean isCompatible(EventLoop loop) {
154 return loop instanceof SingleThreadEventLoop;
155 }
156
157 @Override
158 protected SocketAddress localAddress0() {
159 return localAddress;
160 }
161
162 @Override
163 protected SocketAddress remoteAddress0() {
164 return remoteAddress;
165 }
166
167 @Override
168 protected void doRegister() throws Exception {
169
170
171
172
173
174
175
176
177 final LocalChannel peer = LocalChannel.this.peer;
178 if (peer != null && parent() != null) {
179
180 State oldState = state;
181 state = State.CONNECTED;
182
183
184
185
186 if (STATE_UPDATER.compareAndSet(peer, State.BOUND, State.CONNECTED) ||
187 peer.state == State.CONNECTED) {
188 peer.remoteAddress = parent().localAddress();
189
190
191
192
193
194 peer.eventLoop().execute(new Runnable() {
195 @Override
196 public void run() {
197 ChannelPromise promise = peer.connectPromise;
198
199
200
201 if (promise != null && promise.trySuccess()) {
202 peer.pipeline().fireChannelActive();
203 }
204 }
205 });
206 } else {
207
208
209 LocalChannel.this.peer = null;
210 state = oldState;
211 eventLoop().execute(new Runnable() {
212 @Override
213 public void run() {
214 unsafe().close(unsafe().voidPromise());
215 }
216 });
217 }
218 }
219 ((SingleThreadEventExecutor) eventLoop()).addShutdownHook(shutdownHook);
220 }
221
222 @Override
223 protected void doBind(SocketAddress localAddress) throws Exception {
224 this.localAddress =
225 LocalChannelRegistry.register(this, this.localAddress,
226 localAddress);
227 state = State.BOUND;
228 }
229
230 @Override
231 protected void doDisconnect() throws Exception {
232 doClose();
233 }
234
235 @Override
236 protected void doClose() throws Exception {
237 final LocalChannel peer = this.peer;
238 State oldState = state;
239 State closedFrom = oldState;
240 try {
241 if (oldState != State.CLOSED) {
242
243 if (localAddress != null) {
244 if (parent() == null) {
245 LocalChannelRegistry.unregister(localAddress);
246 }
247 localAddress = null;
248 }
249
250
251
252
253
254 closedFrom = STATE_UPDATER.getAndSet(this, State.CLOSED);
255
256
257 if (writeInProgress && peer != null) {
258 finishPeerRead(peer);
259 }
260
261 ChannelPromise promise = connectPromise;
262 if (promise != null) {
263
264 promise.tryFailure(new ClosedChannelException());
265 connectPromise = null;
266 }
267 }
268
269 if (peer != null && parent() == null && closedFrom != State.CONNECTED) {
270
271
272
273
274 this.peer = null;
275 } else if (peer != null) {
276 this.peer = null;
277
278
279
280 EventLoop peerEventLoop = peer.eventLoop();
281 final boolean peerIsActive = peer.isActive();
282 try {
283 peerEventLoop.execute(new Runnable() {
284 @Override
285 public void run() {
286 peer.tryClose(peerIsActive);
287 }
288 });
289 } catch (Throwable cause) {
290 logger.warn("Releasing Inbound Queues for channels {}-{} because exception occurred!",
291 this, peer, cause);
292 if (peerEventLoop.inEventLoop()) {
293 peer.releaseInboundBuffers();
294 } else {
295
296
297 peer.close();
298 }
299 PlatformDependent.throwException(cause);
300 }
301 }
302 } finally {
303
304 if (oldState != null && oldState != State.CLOSED) {
305
306
307
308
309 releaseInboundBuffers();
310 }
311 }
312 }
313
314 private void tryClose(boolean isActive) {
315 if (isActive) {
316
317
318
319
320
321 finishPeerRead0(this);
322 readInboundBeforeClose();
323 unsafe().close(unsafe().voidPromise());
324 } else if (connectPromise != null) {
325
326
327
328 ChannelPromise promise = connectPromise;
329 connectPromise = null;
330 peer = null;
331 promise.tryFailure(new ConnectException("connection refused: the accepted channel was closed"));
332 unsafe().close(unsafe().voidPromise());
333 } else {
334 releaseInboundBuffers();
335 }
336 }
337
338 private void readInboundBeforeClose() {
339
340
341 while (!inboundBuffer.isEmpty() && isOpen()) {
342 readInProgress = false;
343 readInbound();
344 }
345 }
346
347 @Override
348 protected void doDeregister() throws Exception {
349
350 ((SingleThreadEventExecutor) eventLoop()).removeShutdownHook(shutdownHook);
351 }
352
353 private void readInbound() {
354 RecvByteBufAllocator.Handle handle = unsafe().recvBufAllocHandle();
355 handle.reset(config());
356 ChannelPipeline pipeline = pipeline();
357 do {
358 Object received = inboundBuffer.poll();
359 if (received == null) {
360 break;
361 }
362 if (received instanceof ByteBuf && inboundBuffer.peek() instanceof ByteBuf) {
363 ByteBuf msg = (ByteBuf) received;
364 ByteBuf output = handle.allocate(alloc());
365 if (msg.readableBytes() < output.writableBytes()) {
366
367 output.writeBytes(msg, msg.readerIndex(), msg.readableBytes());
368 msg.release();
369 while ((received = inboundBuffer.peek()) instanceof ByteBuf &&
370 ((ByteBuf) received).readableBytes() < output.writableBytes()) {
371 inboundBuffer.poll();
372 msg = (ByteBuf) received;
373 output.writeBytes(msg, msg.readerIndex(), msg.readableBytes());
374 msg.release();
375 }
376 handle.lastBytesRead(output.readableBytes());
377 received = output;
378 } else {
379
380 handle.lastBytesRead(output.capacity());
381 output.release();
382 }
383 }
384 handle.incMessagesRead(1);
385 pipeline.fireChannelRead(received);
386 } while (handle.continueReading());
387 handle.readComplete();
388 pipeline.fireChannelReadComplete();
389 }
390
391 @Override
392 protected void doBeginRead() throws Exception {
393 if (readInProgress) {
394 return;
395 }
396
397 Queue<Object> inboundBuffer = this.inboundBuffer;
398 if (inboundBuffer.isEmpty()) {
399 readInProgress = true;
400 return;
401 }
402
403 final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
404 final int stackDepth = threadLocals.localChannelReaderStackDepth();
405 if (stackDepth < MAX_READER_STACK_DEPTH) {
406 threadLocals.setLocalChannelReaderStackDepth(stackDepth + 1);
407 try {
408 readInbound();
409 } finally {
410 threadLocals.setLocalChannelReaderStackDepth(stackDepth);
411 }
412 } else {
413 try {
414 eventLoop().execute(readTask);
415 } catch (Throwable cause) {
416 logger.warn("Closing Local channels {}-{} because exception occurred!", this, peer, cause);
417 close();
418 peer.close();
419 PlatformDependent.throwException(cause);
420 }
421 }
422 }
423
424 @Override
425 protected void doWrite(ChannelOutboundBuffer in) throws Exception {
426 switch (state) {
427 case OPEN:
428 case BOUND:
429 throw new NotYetConnectedException();
430 case CLOSED:
431 throw new ClosedChannelException();
432 case CONNECTED:
433 break;
434 }
435
436 final LocalChannel peer = this.peer;
437
438 writeInProgress = true;
439 try {
440 ClosedChannelException exception = null;
441 for (;;) {
442 Object msg = in.current();
443 if (msg == null) {
444 break;
445 }
446 try {
447
448
449 if (peer.state == State.CONNECTED) {
450 peer.inboundBuffer.add(ReferenceCountUtil.retain(msg));
451 in.remove();
452 } else {
453 if (exception == null) {
454 exception = new ClosedChannelException();
455 }
456 in.remove(exception);
457 }
458 } catch (Throwable cause) {
459 in.remove(cause);
460 }
461 }
462 } finally {
463
464
465
466
467
468 writeInProgress = false;
469 }
470
471 finishPeerRead(peer);
472 }
473
474 private void finishPeerRead(final LocalChannel peer) {
475
476 if (peer.eventLoop() == eventLoop() && !peer.writeInProgress) {
477 finishPeerRead0(peer);
478 } else {
479 runFinishPeerReadTask(peer);
480 }
481 }
482
483 private void runFinishTask0() {
484
485
486 if (writeInProgress) {
487 finishReadFuture = eventLoop().submit(finishReadTask);
488 } else {
489 eventLoop().execute(finishReadTask);
490 }
491 }
492
493 private void runFinishPeerReadTask(final LocalChannel peer) {
494 try {
495 peer.runFinishTask0();
496 } catch (Throwable cause) {
497 logger.warn("Closing Local channels {}-{} because exception occurred!", this, peer, cause);
498 close();
499 peer.close();
500 PlatformDependent.throwException(cause);
501 }
502 }
503
504 private void releaseInboundBuffers() {
505 assert eventLoop() == null || eventLoop().inEventLoop();
506 readInProgress = false;
507 Queue<Object> inboundBuffer = this.inboundBuffer;
508 Object msg;
509 while ((msg = inboundBuffer.poll()) != null) {
510 ReferenceCountUtil.release(msg);
511 }
512 }
513
514 private void finishPeerRead0(LocalChannel peer) {
515 Future<?> peerFinishReadFuture = peer.finishReadFuture;
516 if (peerFinishReadFuture != null) {
517 if (!peerFinishReadFuture.isDone()) {
518 runFinishPeerReadTask(peer);
519 return;
520 } else {
521
522 FINISH_READ_FUTURE_UPDATER.compareAndSet(peer, peerFinishReadFuture, null);
523 }
524 }
525
526
527 if (peer.readInProgress && !peer.inboundBuffer.isEmpty()) {
528 peer.readInProgress = false;
529 peer.readInbound();
530 }
531 }
532
533 private class LocalUnsafe extends AbstractUnsafe {
534
535 @Override
536 public void connect(final SocketAddress remoteAddress,
537 SocketAddress localAddress, final ChannelPromise promise) {
538 if (!promise.setUncancellable() || !ensureOpen(promise)) {
539 return;
540 }
541
542 if (state == State.CONNECTED) {
543 Exception cause = new AlreadyConnectedException();
544 safeSetFailure(promise, cause);
545 return;
546 }
547
548 if (connectPromise != null) {
549 throw new ConnectionPendingException();
550 }
551
552 connectPromise = promise;
553
554 if (state != State.BOUND) {
555
556 if (localAddress == null) {
557 localAddress = new LocalAddress(LocalChannel.this);
558 }
559 }
560
561 if (localAddress != null) {
562 try {
563 doBind(localAddress);
564 } catch (Throwable t) {
565 safeSetFailure(promise, t);
566 close(voidPromise());
567 return;
568 }
569 }
570
571 Channel boundChannel = LocalChannelRegistry.get(remoteAddress);
572 if (!(boundChannel instanceof LocalServerChannel)) {
573 Exception cause = new ConnectException("connection refused: " + remoteAddress);
574 safeSetFailure(promise, cause);
575 close(voidPromise());
576 return;
577 }
578
579 LocalServerChannel serverChannel = (LocalServerChannel) boundChannel;
580 peer = serverChannel.serve(LocalChannel.this);
581 }
582 }
583 }