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