/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.channel.epoll;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.unix.IovArray;
import io.netty.util.concurrent.FastThreadLocal;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import static io.netty.channel.unix.Limits.UIO_MAX_IOV;
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
Support sendmmsg(...) on linux with GLIBC 2.14+
/**
* Support <a href="http://linux.die.net/man/2/sendmmsg">sendmmsg(...)</a> on linux with GLIBC 2.14+
*/
final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessageProcessor {
private static final FastThreadLocal<NativeDatagramPacketArray> ARRAY =
new FastThreadLocal<NativeDatagramPacketArray>() {
@Override
protected NativeDatagramPacketArray initialValue() throws Exception {
return new NativeDatagramPacketArray();
}
@Override
protected void onRemoval(NativeDatagramPacketArray value) throws Exception {
NativeDatagramPacket[] packetsArray = value.packets;
// Release all packets
for (NativeDatagramPacket datagramPacket : packetsArray) {
datagramPacket.release();
}
}
};
// Use UIO_MAX_IOV as this is the maximum number we can write with one sendmmsg(...) call.
private final NativeDatagramPacket[] packets = new NativeDatagramPacket[UIO_MAX_IOV];
private int count;
private NativeDatagramPacketArray() {
for (int i = 0; i < packets.length; i++) {
packets[i] = new NativeDatagramPacket();
}
}
/**
* Try to add the given {@link DatagramPacket}. Returns {@code true} on success,
* {@code false} otherwise.
*/
boolean add(DatagramPacket packet) {
if (count == packets.length) {
return false;
}
ByteBuf content = packet.content();
int len = content.readableBytes();
if (len == 0) {
return true;
}
NativeDatagramPacket p = packets[count];
InetSocketAddress recipient = packet.recipient();
if (!p.init(content, recipient)) {
return false;
}
count++;
return true;
}
@Override
public boolean processMessage(Object msg) throws Exception {
return msg instanceof DatagramPacket && add((DatagramPacket) msg);
}
Returns the count
/**
* Returns the count
*/
int count() {
return count;
}
Returns an array with count()
NativeDatagramPacket
s filled. /**
* Returns an array with {@link #count()} {@link NativeDatagramPacket}s filled.
*/
NativeDatagramPacket[] packets() {
return packets;
}
Returns a NativeDatagramPacketArray
which is filled with the flushed messages of ChannelOutboundBuffer
. /**
* Returns a {@link NativeDatagramPacketArray} which is filled with the flushed messages of
* {@link ChannelOutboundBuffer}.
*/
static NativeDatagramPacketArray getInstance(ChannelOutboundBuffer buffer) throws Exception {
NativeDatagramPacketArray array = ARRAY.get();
array.count = 0;
buffer.forEachFlushedMessage(array);
return array;
}
Used to pass needed data to JNI.
/**
* Used to pass needed data to JNI.
*/
@SuppressWarnings("unused")
static final class NativeDatagramPacket {
// Each NativeDatagramPackets holds a IovArray which is used for gathering writes.
// This is ok as NativeDatagramPacketArray is always obtained via a FastThreadLocal and
// so the memory needed is quite small anyway.
private final IovArray array = new IovArray();
// This is the actual struct iovec*
private long memoryAddress;
private int count;
private byte[] addr;
private int scopeId;
private int port;
private void release() {
array.release();
}
Init this instance and return true
if the init was successful. /**
* Init this instance and return {@code true} if the init was successful.
*/
private boolean init(ByteBuf buf, InetSocketAddress recipient) {
array.clear();
if (!array.add(buf)) {
return false;
}
// always start from offset 0
memoryAddress = array.memoryAddress(0);
count = array.count();
InetAddress address = recipient.getAddress();
if (address instanceof Inet6Address) {
addr = address.getAddress();
scopeId = ((Inet6Address) address).getScopeId();
} else {
addr = ipv4MappedIpv6Address(address.getAddress());
scopeId = 0;
}
port = recipient.getPort();
return true;
}
}
}