View Javadoc
1   /*
2    * Copyright 2012 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    *   https://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.local;
17  
18  import io.netty.channel.AbstractServerChannel;
19  import io.netty.channel.ChannelConfig;
20  import io.netty.channel.ChannelPipeline;
21  import io.netty.channel.ChannelPromise;
22  import io.netty.channel.DefaultChannelConfig;
23  import io.netty.channel.EventLoop;
24  import io.netty.channel.IoEvent;
25  import io.netty.channel.IoEventLoop;
26  import io.netty.channel.IoEventLoopGroup;
27  import io.netty.channel.IoRegistration;
28  import io.netty.channel.PreferHeapByteBufAllocator;
29  import io.netty.channel.RecvByteBufAllocator;
30  import io.netty.channel.ServerChannel;
31  import io.netty.channel.ServerChannelRecvByteBufAllocator;
32  import io.netty.channel.SingleThreadEventLoop;
33  import io.netty.util.concurrent.SingleThreadEventExecutor;
34  
35  import java.net.SocketAddress;
36  import java.util.ArrayDeque;
37  import java.util.Queue;
38  
39  /**
40   * A {@link ServerChannel} for the local transport which allows in VM communication.
41   */
42  public class LocalServerChannel extends AbstractServerChannel {
43  
44      private final ChannelConfig config =
45              new DefaultChannelConfig(this, new ServerChannelRecvByteBufAllocator()) { };
46      private final Queue<Object> inboundBuffer = new ArrayDeque<Object>();
47  
48      private IoRegistration registration;
49  
50      private volatile int state; // 0 - open, 1 - active, 2 - closed
51      private volatile LocalAddress localAddress;
52      private volatile boolean acceptInProgress;
53  
54      public LocalServerChannel() {
55          config().setAllocator(new PreferHeapByteBufAllocator(config.getAllocator()));
56      }
57  
58      @Override
59      public ChannelConfig config() {
60          return config;
61      }
62  
63      @Override
64      public LocalAddress localAddress() {
65          return (LocalAddress) super.localAddress();
66      }
67  
68      @Override
69      public LocalAddress remoteAddress() {
70          return (LocalAddress) super.remoteAddress();
71      }
72  
73      @Override
74      public boolean isOpen() {
75          return state < 2;
76      }
77  
78      @Override
79      public boolean isActive() {
80          return state == 1;
81      }
82  
83      @Override
84      protected boolean isCompatible(EventLoop loop) {
85          return loop instanceof SingleThreadEventLoop ||
86                  (loop instanceof IoEventLoop && ((IoEventLoop) loop).isCompatible(LocalServerUnsafe.class));
87      }
88  
89      @Override
90      protected SocketAddress localAddress0() {
91          return localAddress;
92      }
93  
94      @Override
95      protected void doRegister(ChannelPromise promise) {
96          EventLoop loop = eventLoop();
97          if (loop instanceof IoEventLoop) {
98              assert registration == null;
99              ((IoEventLoop) loop).register((LocalServerUnsafe) unsafe()).addListener(f -> {
100                 if (f.isSuccess()) {
101                     registration = (IoRegistration) f.getNow();
102                     promise.setSuccess();
103                 } else {
104                     promise.setFailure(f.cause());
105                 }
106             });
107         } else {
108             try {
109                 ((LocalServerUnsafe) unsafe()).registered();
110             } catch (Throwable cause) {
111                 promise.setFailure(cause);
112                 return;
113             }
114             promise.setSuccess();
115         }
116     }
117 
118     @Override
119     protected void doBind(SocketAddress localAddress) throws Exception {
120         this.localAddress = LocalChannelRegistry.register(this, this.localAddress, localAddress);
121         state = 1;
122     }
123 
124     @Override
125     protected void doClose() throws Exception {
126         if (state <= 1) {
127             // Update all internal state before the closeFuture is notified.
128             if (localAddress != null) {
129                 LocalChannelRegistry.unregister(localAddress);
130                 localAddress = null;
131             }
132             state = 2;
133         }
134 
135         // Close connections that were queued but never accepted, so their connect attempt fails instead of
136         // never completing.
137         Object m;
138         while ((m = inboundBuffer.poll()) != null) {
139             ((LocalChannel) m).unsafe().closeForcibly();
140         }
141     }
142 
143     @Override
144     protected void doDeregister() throws Exception {
145         EventLoop loop = eventLoop();
146         if (loop instanceof IoEventLoop) {
147             IoRegistration registration = this.registration;
148             if (registration != null) {
149                 this.registration = null;
150                 registration.cancel();
151             }
152         } else {
153             ((LocalServerUnsafe) unsafe()).unregistered();
154         }
155     }
156 
157     @Override
158     protected void doBeginRead() throws Exception {
159         if (acceptInProgress) {
160             return;
161         }
162 
163         Queue<Object> inboundBuffer = this.inboundBuffer;
164         if (inboundBuffer.isEmpty()) {
165             acceptInProgress = true;
166             return;
167         }
168 
169         readInbound();
170     }
171 
172     LocalChannel serve(final LocalChannel peer) {
173         final LocalChannel child = newLocalChannel(peer);
174         if (eventLoop().inEventLoop()) {
175             serve0(child);
176         } else {
177             eventLoop().execute(new Runnable() {
178                 @Override
179                 public void run() {
180                     serve0(child);
181                 }
182             });
183         }
184         return child;
185     }
186 
187     private void readInbound() {
188         RecvByteBufAllocator.Handle handle = unsafe().recvBufAllocHandle();
189         handle.reset(config());
190         ChannelPipeline pipeline = pipeline();
191         do {
192             Object m = inboundBuffer.poll();
193             if (m == null) {
194                 break;
195             }
196             handle.incMessagesRead(1);
197             pipeline.fireChannelRead(m);
198         } while (handle.continueReading());
199         handle.readComplete();
200         pipeline.fireChannelReadComplete();
201     }
202 
203     /**
204      * A factory method for {@link LocalChannel}s. Users may override it
205      * to create custom instances of {@link LocalChannel}s.
206      */
207     protected LocalChannel newLocalChannel(LocalChannel peer) {
208         return new LocalChannel(this, peer);
209     }
210 
211     private void serve0(final LocalChannel child) {
212         if (!isOpen()) {
213             child.unsafe().closeForcibly();
214             return;
215         }
216         inboundBuffer.add(child);
217         if (acceptInProgress) {
218             acceptInProgress = false;
219 
220             readInbound();
221         }
222     }
223 
224     @Override
225     protected AbstractUnsafe newUnsafe() {
226         return new LocalServerUnsafe();
227     }
228 
229     private class LocalServerUnsafe extends AbstractUnsafe implements LocalIoHandle {
230         private final Runnable shutdownHook = this::closeNow;
231 
232         @Override
233         public void close() {
234             close(voidPromise());
235         }
236 
237         @Override
238         public void handle(IoRegistration registration, IoEvent event) {
239             // NOOP
240         }
241 
242         @Override
243         public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
244             safeSetFailure(promise, new UnsupportedOperationException());
245         }
246 
247         @Override
248         public void registered() {
249             EventLoop loop = eventLoop();
250             if (!(loop instanceof IoEventLoop) && loop instanceof SingleThreadEventExecutor) {
251                 ((SingleThreadEventExecutor) eventLoop()).addShutdownHook(shutdownHook);
252             }
253         }
254 
255         @Override
256         public void unregistered() {
257             EventLoop loop = eventLoop();
258             if (!(loop instanceof IoEventLoop) && loop instanceof SingleThreadEventExecutor) {
259                 ((SingleThreadEventExecutor) eventLoop()).removeShutdownHook(shutdownHook);
260             }
261         }
262 
263         @Override
264         public void closeNow() {
265             close(voidPromise());
266         }
267     }
268 }