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      * Replaces the current {@link Selector} of this event loop with newly created {@link Selector}s to work
142      * around the infamous epoll 100% CPU bug.
143      */
144     public void rebuildSelector() {
145         if (!inEventLoop()) {
146             execute(new Runnable() {
147                 @Override
148                 public void run() {
149                     ((NioIoHandler) ioHandler()).rebuildSelector0();
150                 }
151             });
152             return;
153         }
154         ((NioIoHandler) ioHandler()).rebuildSelector0();
155     }
156 
157     @Override
158     public int registeredChannels() {
159         return ((NioIoHandler) ioHandler()).numRegistered();
160     }
161 
162     @Override
163     public Iterator<Channel> registeredChannelsIterator() {
164         assert inEventLoop();
165         final Set<SelectionKey> keys =  ((NioIoHandler) ioHandler()).registeredSet();
166         if (keys.isEmpty()) {
167             return ChannelsReadOnlyIterator.empty();
168         }
169         return new Iterator<Channel>() {
170             final Iterator<SelectionKey> selectionKeyIterator =
171                     ObjectUtil.checkNotNull(keys, "selectionKeys")
172                             .iterator();
173             Channel next;
174             boolean isDone;
175 
176             @Override
177             public boolean hasNext() {
178                 if (isDone) {
179                     return false;
180                 }
181                 Channel cur = next;
182                 if (cur == null) {
183                     cur = next = nextOrDone();
184                     return cur != null;
185                 }
186                 return true;
187             }
188 
189             @Override
190             public Channel next() {
191                 if (isDone) {
192                     throw new NoSuchElementException();
193                 }
194                 Channel cur = next;
195                 if (cur == null) {
196                     cur = nextOrDone();
197                     if (cur == null) {
198                         throw new NoSuchElementException();
199                     }
200                 }
201                 next = nextOrDone();
202                 return cur;
203             }
204 
205             @Override
206             public void remove() {
207                 throw new UnsupportedOperationException("remove");
208             }
209 
210             private Channel nextOrDone() {
211                 Iterator<SelectionKey> it = selectionKeyIterator;
212                 while (it.hasNext()) {
213                     SelectionKey key = it.next();
214                     if (key.isValid()) {
215                         Object attachment = key.attachment();
216                         if (attachment instanceof NioIoHandler.DefaultNioRegistration) {
217                             NioIoHandle handle =  ((NioIoHandler.DefaultNioRegistration) attachment).handle();
218                             if (handle instanceof AbstractNioChannel.AbstractNioUnsafe) {
219                                 return ((AbstractNioChannel.AbstractNioUnsafe) handle).channel();
220                             }
221                         }
222                     }
223                 }
224                 isDone = true;
225                 return null;
226             }
227         };
228     }
229 
230     Selector unwrappedSelector() {
231         return ((NioIoHandler) ioHandler()).unwrappedSelector();
232     }
233 }