View Javadoc
1   /*
2    * Copyright 2024 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.nio;
17  
18  import io.netty.channel.Channel;
19  import io.netty.channel.EventLoopTaskQueueFactory;
20  import io.netty.channel.IoHandlerFactory;
21  import io.netty.channel.IoRegistration;
22  import io.netty.channel.SingleThreadIoEventLoop;
23  import io.netty.util.concurrent.RejectedExecutionHandler;
24  import io.netty.util.internal.ObjectUtil;
25  import io.netty.util.internal.logging.InternalLogger;
26  import io.netty.util.internal.logging.InternalLoggerFactory;
27  
28  import java.nio.channels.SelectableChannel;
29  import java.nio.channels.SelectionKey;
30  import java.nio.channels.Selector;
31  import java.nio.channels.spi.SelectorProvider;
32  import java.util.Iterator;
33  import java.util.NoSuchElementException;
34  import java.util.Queue;
35  import java.util.Set;
36  import java.util.concurrent.Executor;
37  
38  /**
39   * {@link SingleThreadIoEventLoop} implementation which register the {@link Channel}'s to a
40   * {@link Selector} and so does the multi-plexing of these in the event loop.
41   *
42   * @deprecated Use {@link SingleThreadIoEventLoop} with {@link NioIoHandler}
43   */
44  @Deprecated
45  public final class NioEventLoop extends SingleThreadIoEventLoop {
46  
47      private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioEventLoop.class);
48  
49      NioEventLoop(NioEventLoopGroup parent, Executor executor, IoHandlerFactory ioHandlerFactory,
50                   EventLoopTaskQueueFactory taskQueueFactory,
51                   EventLoopTaskQueueFactory tailTaskQueueFactory, RejectedExecutionHandler rejectedExecutionHandler) {
52          super(parent, executor, ioHandlerFactory, newTaskQueue(taskQueueFactory), newTaskQueue(tailTaskQueueFactory),
53                  rejectedExecutionHandler);
54      }
55  
56      private static Queue<Runnable> newTaskQueue(
57              EventLoopTaskQueueFactory queueFactory) {
58          if (queueFactory == null) {
59              return newTaskQueue0(DEFAULT_MAX_PENDING_TASKS);
60          }
61          return queueFactory.newTaskQueue(DEFAULT_MAX_PENDING_TASKS);
62      }
63  
64      /**
65       * Returns the {@link SelectorProvider} used by this {@link NioEventLoop} to obtain the {@link Selector}.
66       */
67      public SelectorProvider selectorProvider() {
68          return ((NioIoHandler) ioHandler()).selectorProvider();
69      }
70  
71      /**
72       * Registers an arbitrary {@link SelectableChannel}, not necessarily created by Netty, to the {@link Selector}
73       * of this event loop.  Once the specified {@link SelectableChannel} is registered, the specified {@code task} will
74       * be executed by this event loop when the {@link SelectableChannel} is ready.
75       */
76      public void register(final SelectableChannel ch, final int interestOps, final NioTask<?> task) {
77          ObjectUtil.checkNotNull(ch, "ch");
78          if (interestOps == 0) {
79              throw new IllegalArgumentException("interestOps must be non-zero.");
80          }
81          if ((interestOps & ~ch.validOps()) != 0) {
82              throw new IllegalArgumentException(
83                      "invalid interestOps: " + interestOps + "(validOps: " + ch.validOps() + ')');
84          }
85          ObjectUtil.checkNotNull(task, "task");
86  
87          if (isShutdown()) {
88              throw new IllegalStateException("event loop shut down");
89          }
90  
91          @SuppressWarnings("unchecked")
92          final NioTask<SelectableChannel> nioTask = (NioTask<SelectableChannel>) task;
93          if (inEventLoop()) {
94              register0(ch, interestOps, nioTask);
95          } else {
96              try {
97                  // Offload to the EventLoop as otherwise java.nio.channels.spi.AbstractSelectableChannel.register
98                  // may block for a long time while trying to obtain an internal lock that may be hold while selecting.
99                  submit(new Runnable() {
100                     @Override
101                     public void run() {
102                         register0(ch, interestOps, nioTask);
103                     }
104                 }).sync();
105             } catch (InterruptedException ignore) {
106                 // Even if interrupted we did schedule it so just mark the Thread as interrupted.
107                 Thread.currentThread().interrupt();
108             }
109         }
110     }
111 
112     private void register0(final SelectableChannel ch, int interestOps, final NioTask<SelectableChannel> task) {
113         try {
114             IoRegistration registration = register(
115                     new NioSelectableChannelIoHandle<SelectableChannel>(ch) {
116                         @Override
117                         protected void handle(SelectableChannel channel, SelectionKey key) {
118                             try {
119                                 task.channelReady(channel, key);
120                             } catch (Exception e) {
121                                 logger.warn("Unexpected exception while running NioTask.channelReady(...)", e);
122                             }
123                         }
124 
125                         @Override
126                         protected void deregister(SelectableChannel channel) {
127                             try {
128                                 task.channelUnregistered(channel, null);
129                             } catch (Exception e) {
130                                 logger.warn("Unexpected exception while running NioTask.channelUnregistered(...)", e);
131                             }
132                         }
133                     }).get();
134             registration.submit(NioIoOps.valueOf(interestOps));
135         } catch (Exception e) {
136             throw new IllegalStateException(e);
137         }
138     }
139 
140     /**
141      * Always return 0.
142      */
143     public int getIoRatio() {
144         return 0;
145     }
146 
147     /**
148      * This method is a no-op.
149      *
150      * @deprecated
151      */
152     @Deprecated
153     public void setIoRatio(int ioRatio) {
154         logger.debug("NioEventLoop.setIoRatio(int) logic was removed, this is a no-op");
155     }
156 
157     /**
158      * Replaces the current {@link Selector} of this event loop with newly created {@link Selector}s to work
159      * around the infamous epoll 100% CPU bug.
160      */
161     public void rebuildSelector() {
162         if (!inEventLoop()) {
163             execute(new Runnable() {
164                 @Override
165                 public void run() {
166                     ((NioIoHandler) ioHandler()).rebuildSelector0();
167                 }
168             });
169             return;
170         }
171         ((NioIoHandler) ioHandler()).rebuildSelector0();
172     }
173 
174     @Override
175     public int registeredChannels() {
176         return ((NioIoHandler) ioHandler()).numRegistered();
177     }
178 
179     @Override
180     public Iterator<Channel> registeredChannelsIterator() {
181         assert inEventLoop();
182         final Set<SelectionKey> keys =  ((NioIoHandler) ioHandler()).registeredSet();
183         if (keys.isEmpty()) {
184             return ChannelsReadOnlyIterator.empty();
185         }
186         return new Iterator<Channel>() {
187             final Iterator<SelectionKey> selectionKeyIterator =
188                     ObjectUtil.checkNotNull(keys, "selectionKeys")
189                             .iterator();
190             Channel next;
191             boolean isDone;
192 
193             @Override
194             public boolean hasNext() {
195                 if (isDone) {
196                     return false;
197                 }
198                 Channel cur = next;
199                 if (cur == null) {
200                     cur = next = nextOrDone();
201                     return cur != null;
202                 }
203                 return true;
204             }
205 
206             @Override
207             public Channel next() {
208                 if (isDone) {
209                     throw new NoSuchElementException();
210                 }
211                 Channel cur = next;
212                 if (cur == null) {
213                     cur = nextOrDone();
214                     if (cur == null) {
215                         throw new NoSuchElementException();
216                     }
217                 }
218                 next = nextOrDone();
219                 return cur;
220             }
221 
222             @Override
223             public void remove() {
224                 throw new UnsupportedOperationException("remove");
225             }
226 
227             private Channel nextOrDone() {
228                 Iterator<SelectionKey> it = selectionKeyIterator;
229                 while (it.hasNext()) {
230                     SelectionKey key = it.next();
231                     if (key.isValid()) {
232                         Object attachment = key.attachment();
233                         if (attachment instanceof NioIoHandler.DefaultNioRegistration) {
234                             NioIoHandle handle =  ((NioIoHandler.DefaultNioRegistration) attachment).handle();
235                             if (handle instanceof AbstractNioChannel.AbstractNioUnsafe) {
236                                 return ((AbstractNioChannel.AbstractNioUnsafe) handle).channel();
237                             }
238                         }
239                     }
240                 }
241                 isDone = true;
242                 return null;
243             }
244         };
245     }
246 
247     Selector unwrappedSelector() {
248         return ((NioIoHandler) ioHandler()).unwrappedSelector();
249     }
250 }