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