View Javadoc
1   /*
2    * Copyright 2014 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    *   http://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.epoll;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelOutboundBuffer;
20  import io.netty.channel.socket.DatagramPacket;
21  import io.netty.channel.unix.IovArray;
22  import java.net.Inet6Address;
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  
26  import static io.netty.channel.unix.Limits.UIO_MAX_IOV;
27  import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
28  
29  /**
30   * Support <a href="http://linux.die.net/man/2/sendmmsg">sendmmsg(...)</a> on linux with GLIBC 2.14+
31   */
32  final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessageProcessor {
33  
34      // Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call.
35      private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV];
36  
37      // We share one IovArray for all NativeDatagramPackets to reduce memory overhead. This will allow us to write
38      // up to IOV_MAX iovec across all messages in one sendmmsg(...) call.
39      private final IovArray iovArray = new IovArray();
40      private int count;
41  
42      NativeDatagramPacketArray() {
43          for (int i = 0; i < packets.length; i++) {
44              packets[i] = new NativeDatagramPacket();
45          }
46      }
47  
48      /**
49       * Try to add the given {@link DatagramPacket}. Returns {@code true} on success,
50       * {@code false} otherwise.
51       */
52      boolean add(DatagramPacket packet) {
53          if (count == packets.length) {
54              // We already filled up to UIO_MAX_IOV messages. This is the max allowed per sendmmsg(...) call, we will
55              // try again later.
56              return false;
57          }
58          ByteBuf content = packet.content();
59          int len = content.readableBytes();
60          if (len == 0) {
61              return true;
62          }
63          NativeDatagramPacket p = packets[count];
64          InetSocketAddress recipient = packet.recipient();
65  
66          int offset = iovArray.count();
67          if (!iovArray.add(content)) {
68              // Not enough space to hold the whole content, we will try again later.
69              return false;
70          }
71          p.init(iovArray.memoryAddress(offset), iovArray.count() - offset, recipient);
72  
73          count++;
74          return true;
75      }
76  
77      @Override
78      public boolean processMessage(Object msg) {
79          return msg instanceof DatagramPacket && add((DatagramPacket) msg);
80      }
81  
82      /**
83       * Returns the count
84       */
85      int count() {
86          return count;
87      }
88  
89      /**
90       * Returns an array with {@link #count()} {@link NativeDatagramPacket}s filled.
91       */
92      NativeDatagramPacket[] packets() {
93          return packets;
94      }
95  
96      void clear() {
97          this.count = 0;
98          this.iovArray.clear();
99      }
100 
101     void release() {
102         iovArray.release();
103     }
104 
105     /**
106      * Used to pass needed data to JNI.
107      */
108     @SuppressWarnings("unused")
109     static final class NativeDatagramPacket {
110 
111         // This is the actual struct iovec*
112         private long memoryAddress;
113         private int count;
114 
115         private byte[] addr;
116         private int scopeId;
117         private int port;
118 
119         private void init(long memoryAddress, int count, InetSocketAddress recipient) {
120             this.memoryAddress = memoryAddress;
121             this.count = count;
122 
123             InetAddress address = recipient.getAddress();
124             if (address instanceof Inet6Address) {
125                 addr = address.getAddress();
126                 scopeId = ((Inet6Address) address).getScopeId();
127             } else {
128                 addr = ipv4MappedIpv6Address(address.getAddress());
129                 scopeId = 0;
130             }
131             port = recipient.getPort();
132         }
133     }
134 }