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 unsafe().close(unsafe().voidPromise());
317 } else if (connectPromise != null) {
318
319
320
321 ChannelPromise promise = connectPromise;
322 connectPromise = null;
323 peer = null;
324 promise.tryFailure(new ConnectException("connection refused: the accepted channel was closed"));
325 unsafe().close(unsafe().voidPromise());
326 } else {
327 releaseInboundBuffers();
328 }
329 }
330
331 @Override
332 protected void doDeregister() throws Exception {
333
334 ((SingleThreadEventExecutor) eventLoop()).removeShutdownHook(shutdownHook);
335 }
336
337 private void readInbound() {
338 RecvByteBufAllocator.Handle handle = unsafe().recvBufAllocHandle();
339 handle.reset(config());
340 ChannelPipeline pipeline = pipeline();
341 do {
342 Object received = inboundBuffer.poll();
343 if (received == null) {
344 break;
345 }
346 if (received instanceof ByteBuf && inboundBuffer.peek() instanceof ByteBuf) {
347 ByteBuf msg = (ByteBuf) received;
348 ByteBuf output = handle.allocate(alloc());
349 if (msg.readableBytes() < output.writableBytes()) {
350
351 output.writeBytes(msg, msg.readerIndex(), msg.readableBytes());
352 msg.release();
353 while ((received = inboundBuffer.peek()) instanceof ByteBuf &&
354 ((ByteBuf) received).readableBytes() < output.writableBytes()) {
355 inboundBuffer.poll();
356 msg = (ByteBuf) received;
357 output.writeBytes(msg, msg.readerIndex(), msg.readableBytes());
358 msg.release();
359 }
360 handle.lastBytesRead(output.readableBytes());
361 received = output;
362 } else {
363
364 handle.lastBytesRead(output.capacity());
365 output.release();
366 }
367 }
368 handle.incMessagesRead(1);
369 pipeline.fireChannelRead(received);
370 } while (handle.continueReading());
371 handle.readComplete();
372 pipeline.fireChannelReadComplete();
373 }
374
375 @Override
376 protected void doBeginRead() throws Exception {
377 if (readInProgress) {
378 return;
379 }
380
381 Queue<Object> inboundBuffer = this.inboundBuffer;
382 if (inboundBuffer.isEmpty()) {
383 readInProgress = true;
384 return;
385 }
386
387 final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
388 final int stackDepth = threadLocals.localChannelReaderStackDepth();
389 if (stackDepth < MAX_READER_STACK_DEPTH) {
390 threadLocals.setLocalChannelReaderStackDepth(stackDepth + 1);
391 try {
392 readInbound();
393 } finally {
394 threadLocals.setLocalChannelReaderStackDepth(stackDepth);
395 }
396 } else {
397 try {
398 eventLoop().execute(readTask);
399 } catch (Throwable cause) {
400 logger.warn("Closing Local channels {}-{} because exception occurred!", this, peer, cause);
401 close();
402 peer.close();
403 PlatformDependent.throwException(cause);
404 }
405 }
406 }
407
408 @Override
409 protected void doWrite(ChannelOutboundBuffer in) throws Exception {
410 switch (state) {
411 case OPEN:
412 case BOUND:
413 throw new NotYetConnectedException();
414 case CLOSED:
415 throw new ClosedChannelException();
416 case CONNECTED:
417 break;
418 }
419
420 final LocalChannel peer = this.peer;
421
422 writeInProgress = true;
423 try {
424 ClosedChannelException exception = null;
425 for (;;) {
426 Object msg = in.current();
427 if (msg == null) {
428 break;
429 }
430 try {
431
432
433 if (peer.state == State.CONNECTED) {
434 peer.inboundBuffer.add(ReferenceCountUtil.retain(msg));
435 in.remove();
436 } else {
437 if (exception == null) {
438 exception = new ClosedChannelException();
439 }
440 in.remove(exception);
441 }
442 } catch (Throwable cause) {
443 in.remove(cause);
444 }
445 }
446 } finally {
447
448
449
450
451
452 writeInProgress = false;
453 }
454
455 finishPeerRead(peer);
456 }
457
458 private void finishPeerRead(final LocalChannel peer) {
459
460 if (peer.eventLoop() == eventLoop() && !peer.writeInProgress) {
461 finishPeerRead0(peer);
462 } else {
463 runFinishPeerReadTask(peer);
464 }
465 }
466
467 private void runFinishTask0() {
468
469
470 if (writeInProgress) {
471 finishReadFuture = eventLoop().submit(finishReadTask);
472 } else {
473 eventLoop().execute(finishReadTask);
474 }
475 }
476
477 private void runFinishPeerReadTask(final LocalChannel peer) {
478 try {
479 peer.runFinishTask0();
480 } catch (Throwable cause) {
481 logger.warn("Closing Local channels {}-{} because exception occurred!", this, peer, cause);
482 close();
483 peer.close();
484 PlatformDependent.throwException(cause);
485 }
486 }
487
488 private void releaseInboundBuffers() {
489 assert eventLoop() == null || eventLoop().inEventLoop();
490 readInProgress = false;
491 Queue<Object> inboundBuffer = this.inboundBuffer;
492 Object msg;
493 while ((msg = inboundBuffer.poll()) != null) {
494 ReferenceCountUtil.release(msg);
495 }
496 }
497
498 private void finishPeerRead0(LocalChannel peer) {
499 Future<?> peerFinishReadFuture = peer.finishReadFuture;
500 if (peerFinishReadFuture != null) {
501 if (!peerFinishReadFuture.isDone()) {
502 runFinishPeerReadTask(peer);
503 return;
504 } else {
505
506 FINISH_READ_FUTURE_UPDATER.compareAndSet(peer, peerFinishReadFuture, null);
507 }
508 }
509
510
511 if (peer.readInProgress && !peer.inboundBuffer.isEmpty()) {
512 peer.readInProgress = false;
513 peer.readInbound();
514 }
515 }
516
517 private class LocalUnsafe extends AbstractUnsafe {
518
519 @Override
520 public void connect(final SocketAddress remoteAddress,
521 SocketAddress localAddress, final ChannelPromise promise) {
522 if (!promise.setUncancellable() || !ensureOpen(promise)) {
523 return;
524 }
525
526 if (state == State.CONNECTED) {
527 Exception cause = new AlreadyConnectedException();
528 safeSetFailure(promise, cause);
529 return;
530 }
531
532 if (connectPromise != null) {
533 throw new ConnectionPendingException();
534 }
535
536 connectPromise = promise;
537
538 if (state != State.BOUND) {
539
540 if (localAddress == null) {
541 localAddress = new LocalAddress(LocalChannel.this);
542 }
543 }
544
545 if (localAddress != null) {
546 try {
547 doBind(localAddress);
548 } catch (Throwable t) {
549 safeSetFailure(promise, t);
550 close(voidPromise());
551 return;
552 }
553 }
554
555 Channel boundChannel = LocalChannelRegistry.get(remoteAddress);
556 if (!(boundChannel instanceof LocalServerChannel)) {
557 Exception cause = new ConnectException("connection refused: " + remoteAddress);
558 safeSetFailure(promise, cause);
559 close(voidPromise());
560 return;
561 }
562
563 LocalServerChannel serverChannel = (LocalServerChannel) boundChannel;
564 peer = serverChannel.serve(LocalChannel.this);
565 }
566 }
567 }