Various minor changes:
- added forwarders to SO objects to Inet - added batch read support for UDP - changed write for UDP - SimpleSender is now registered as everone else
This commit is contained in:
parent
e2ce4644f1
commit
116dcc0e54
14 changed files with 85 additions and 40 deletions
|
|
@ -6,7 +6,6 @@ package akka.io
|
||||||
|
|
||||||
import akka.testkit.AkkaSpec
|
import akka.testkit.AkkaSpec
|
||||||
import akka.util.ByteString
|
import akka.util.ByteString
|
||||||
import akka.io.Inet
|
|
||||||
import Tcp._
|
import Tcp._
|
||||||
import TestUtils._
|
import TestUtils._
|
||||||
import akka.testkit.EventFilter
|
import akka.testkit.EventFilter
|
||||||
|
|
@ -65,8 +64,8 @@ class IntegrationSpec extends AkkaSpec("akka.loglevel = INFO") with IntegrationS
|
||||||
|
|
||||||
expectReceivedData(clientHandler, 100000)
|
expectReceivedData(clientHandler, 100000)
|
||||||
|
|
||||||
override def bindOptions = List(Inet.SO.SendBufferSize(1024))
|
override def bindOptions = List(SO.SendBufferSize(1024))
|
||||||
override def connectOptions = List(Inet.SO.ReceiveBufferSize(1024))
|
override def connectOptions = List(SO.ReceiveBufferSize(1024))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ class UdpConnIntegrationSpec extends AkkaSpec("akka.loglevel = INFO") with Impli
|
||||||
(address, commander.sender)
|
(address, commander.sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
def connectUdp(localAddress: Option[InetSocketAddress], remoteAddress: InetSocketAddress): ActorRef = {
|
def connectUdp(localAddress: Option[InetSocketAddress], remoteAddress: InetSocketAddress, handler: ActorRef): ActorRef = {
|
||||||
val commander = TestProbe()
|
val commander = TestProbe()
|
||||||
commander.send(IO(UdpConn), UdpConn.Connect(testActor, localAddress, remoteAddress, Nil))
|
commander.send(IO(UdpConn), UdpConn.Connect(handler, localAddress, remoteAddress, Nil))
|
||||||
commander.expectMsg(UdpConn.Connected)
|
commander.expectMsg(UdpConn.Connected)
|
||||||
commander.sender
|
commander.sender
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ class UdpConnIntegrationSpec extends AkkaSpec("akka.loglevel = INFO") with Impli
|
||||||
val (serverAddress, server) = bindUdp(testActor)
|
val (serverAddress, server) = bindUdp(testActor)
|
||||||
val data1 = ByteString("To infinity and beyond!")
|
val data1 = ByteString("To infinity and beyond!")
|
||||||
val data2 = ByteString("All your datagram belong to us")
|
val data2 = ByteString("All your datagram belong to us")
|
||||||
connectUdp(localAddress = None, serverAddress) ! UdpConn.Send(data1)
|
connectUdp(localAddress = None, serverAddress, testActor) ! UdpConn.Send(data1)
|
||||||
|
|
||||||
val clientAddress = expectMsgPF() {
|
val clientAddress = expectMsgPF() {
|
||||||
case UdpFF.Received(d, a) ⇒
|
case UdpFF.Received(d, a) ⇒
|
||||||
|
|
@ -55,7 +55,7 @@ class UdpConnIntegrationSpec extends AkkaSpec("akka.loglevel = INFO") with Impli
|
||||||
val (serverAddress, server) = bindUdp(testActor)
|
val (serverAddress, server) = bindUdp(testActor)
|
||||||
val data1 = ByteString("To infinity and beyond!")
|
val data1 = ByteString("To infinity and beyond!")
|
||||||
val data2 = ByteString("All your datagram belong to us")
|
val data2 = ByteString("All your datagram belong to us")
|
||||||
connectUdp(Some(clientAddress), serverAddress) ! UdpConn.Send(data1)
|
connectUdp(Some(clientAddress), serverAddress, testActor) ! UdpConn.Send(data1)
|
||||||
|
|
||||||
expectMsgPF() {
|
expectMsgPF() {
|
||||||
case UdpFF.Received(d, a) ⇒
|
case UdpFF.Received(d, a) ⇒
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class UdpFFIntegrationSpec extends AkkaSpec("akka.loglevel = INFO") with Implici
|
||||||
|
|
||||||
val simpleSender: ActorRef = {
|
val simpleSender: ActorRef = {
|
||||||
val commander = TestProbe()
|
val commander = TestProbe()
|
||||||
commander.send(IO(UdpFF), SimpleSender)
|
commander.send(IO(UdpFF), SimpleSender(Nil))
|
||||||
commander.expectMsg(SimpleSendReady)
|
commander.expectMsg(SimpleSendReady)
|
||||||
commander.sender
|
commander.sender
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -470,6 +470,11 @@ akka {
|
||||||
# this many times before giving up
|
# this many times before giving up
|
||||||
selector-association-retries = 10
|
selector-association-retries = 10
|
||||||
|
|
||||||
|
# The maximum number of datagrams that are read in one go,
|
||||||
|
# higher numbers decrease latency, lower numbers increase fairness on
|
||||||
|
# the worker-dispatcher
|
||||||
|
batch-receive-limit = 3
|
||||||
|
|
||||||
# The number of bytes per direct buffer in the pool used to read or write
|
# The number of bytes per direct buffer in the pool used to read or write
|
||||||
# network data from the kernel.
|
# network data from the kernel.
|
||||||
direct-buffer-size = 128 KiB
|
direct-buffer-size = 128 KiB
|
||||||
|
|
@ -526,6 +531,11 @@ akka {
|
||||||
# this many times before giving up
|
# this many times before giving up
|
||||||
selector-association-retries = 10
|
selector-association-retries = 10
|
||||||
|
|
||||||
|
# The maximum number of datagrams that are read in one go,
|
||||||
|
# higher numbers decrease latency, lower numbers increase fairness on
|
||||||
|
# the worker-dispatcher
|
||||||
|
batch-receive-limit = 3
|
||||||
|
|
||||||
# The number of bytes per direct buffer in the pool used to read or write
|
# The number of bytes per direct buffer in the pool used to read or write
|
||||||
# network data from the kernel.
|
# network data from the kernel.
|
||||||
direct-buffer-size = 128 KiB
|
direct-buffer-size = 128 KiB
|
||||||
|
|
|
||||||
|
|
@ -79,4 +79,11 @@ object Inet {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait SoForwarders {
|
||||||
|
val ReceiveBufferSize = SO.ReceiveBufferSize
|
||||||
|
val ReuseAddress = SO.ReuseAddress
|
||||||
|
val SendBufferSize = SO.SendBufferSize
|
||||||
|
val TrafficClass = SO.TrafficClass
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,7 @@ private[io] class SelectionHandler(manager: ActorRef, settings: SelectionHandler
|
||||||
|
|
||||||
selectorManagementDispatcher.execute(select) // start selection "loop"
|
selectorManagementDispatcher.execute(select) // start selection "loop"
|
||||||
|
|
||||||
|
// FIXME: Add possibility to signal failure of task to someone
|
||||||
abstract class Task extends Runnable {
|
abstract class Task extends Runnable {
|
||||||
def tryRun()
|
def tryRun()
|
||||||
def run() {
|
def run() {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ object Tcp extends ExtensionKey[TcpExt] {
|
||||||
override def get(system: ActorSystem): TcpExt = system.extension(this)
|
override def get(system: ActorSystem): TcpExt = system.extension(this)
|
||||||
|
|
||||||
// shared socket options
|
// shared socket options
|
||||||
object SO {
|
object SO extends Inet.SoForwarders {
|
||||||
|
|
||||||
// general socket options
|
// general socket options
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import akka.actor.{ Props, ActorSystemImpl }
|
||||||
|
|
||||||
object Udp {
|
object Udp {
|
||||||
|
|
||||||
object SO {
|
object SO extends Inet.SoForwarders {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [[akka.io.Inet.SocketOption]] to set the SO_BROADCAST option
|
* [[akka.io.Inet.SocketOption]] to set the SO_BROADCAST option
|
||||||
|
|
@ -29,9 +29,11 @@ object Udp {
|
||||||
val NrOfSelectors = getInt("nr-of-selectors")
|
val NrOfSelectors = getInt("nr-of-selectors")
|
||||||
val DirectBufferSize = getIntBytes("direct-buffer-size")
|
val DirectBufferSize = getIntBytes("direct-buffer-size")
|
||||||
val MaxDirectBufferPoolSize = getInt("max-direct-buffer-pool-size")
|
val MaxDirectBufferPoolSize = getInt("max-direct-buffer-pool-size")
|
||||||
|
val BatchReceiveLimit = getInt("batch-receive-limit")
|
||||||
|
|
||||||
val ManagementDispatcher = getString("management-dispatcher")
|
val ManagementDispatcher = getString("management-dispatcher")
|
||||||
|
|
||||||
|
// FIXME: Use new requiring
|
||||||
require(NrOfSelectors > 0, "nr-of-selectors must be > 0")
|
require(NrOfSelectors > 0, "nr-of-selectors must be > 0")
|
||||||
|
|
||||||
override val MaxChannelsPerSelector = if (MaxChannels == -1) -1 else math.max(MaxChannels / NrOfSelectors, 1)
|
override val MaxChannelsPerSelector = if (MaxChannels == -1) -1 else math.max(MaxChannels / NrOfSelectors, 1)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import java.nio.channels.DatagramChannel
|
||||||
import java.nio.channels.SelectionKey._
|
import java.nio.channels.SelectionKey._
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
private[io] class UdpConnection(selectorRouter: ActorRef,
|
private[io] class UdpConnection(selectorRouter: ActorRef,
|
||||||
handler: ActorRef,
|
handler: ActorRef,
|
||||||
|
|
@ -36,6 +38,7 @@ private[io] class UdpConnection(selectorRouter: ActorRef,
|
||||||
datagramChannel.configureBlocking(false)
|
datagramChannel.configureBlocking(false)
|
||||||
val socket = datagramChannel.socket
|
val socket = datagramChannel.socket
|
||||||
options.foreach(_.beforeDatagramBind(socket))
|
options.foreach(_.beforeDatagramBind(socket))
|
||||||
|
// FIXME: All bind failures have to be reported to the commander in TCP as well
|
||||||
localAddress foreach { socket.bind } // will blow up the actor constructor if the bind fails
|
localAddress foreach { socket.bind } // will blow up the actor constructor if the bind fails
|
||||||
datagramChannel.connect(remoteAddress)
|
datagramChannel.connect(remoteAddress)
|
||||||
datagramChannel
|
datagramChannel
|
||||||
|
|
@ -46,13 +49,14 @@ private[io] class UdpConnection(selectorRouter: ActorRef,
|
||||||
def receive = {
|
def receive = {
|
||||||
case ChannelRegistered ⇒
|
case ChannelRegistered ⇒
|
||||||
bindCommander ! Connected
|
bindCommander ! Connected
|
||||||
|
selector ! ReadInterest
|
||||||
context.become(connected, discardOld = true)
|
context.become(connected, discardOld = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
def connected: Receive = {
|
def connected: Receive = {
|
||||||
case StopReading ⇒ selector ! DisableReadInterest
|
case StopReading ⇒ selector ! DisableReadInterest
|
||||||
case ResumeReading ⇒ selector ! ReadInterest
|
case ResumeReading ⇒ selector ! ReadInterest
|
||||||
case ChannelReadable ⇒ doRead(handler)
|
case ChannelReadable ⇒ println("read"); doRead(handler)
|
||||||
|
|
||||||
case Close ⇒
|
case Close ⇒
|
||||||
log.debug("Closing UDP connection to {}", remoteAddress)
|
log.debug("Closing UDP connection to {}", remoteAddress)
|
||||||
|
|
@ -77,14 +81,17 @@ private[io] class UdpConnection(selectorRouter: ActorRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
def doRead(handler: ActorRef): Unit = {
|
def doRead(handler: ActorRef): Unit = {
|
||||||
val buffer = bufferPool.acquire()
|
@tailrec def innerRead(readsLeft: Int, buffer: ByteBuffer): Unit = {
|
||||||
try {
|
|
||||||
buffer.clear()
|
buffer.clear()
|
||||||
buffer.limit(DirectBufferSize)
|
buffer.limit(DirectBufferSize)
|
||||||
|
|
||||||
if (channel.read(buffer) > 0) handler ! Received(ByteString(buffer))
|
if (channel.read(buffer) > 0) {
|
||||||
|
handler ! Received(ByteString(buffer))
|
||||||
} finally {
|
innerRead(readsLeft - 1, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val buffer = bufferPool.acquire()
|
||||||
|
try innerRead(BatchReceiveLimit, buffer) finally {
|
||||||
selector ! ReadInterest
|
selector ! ReadInterest
|
||||||
bufferPool.release(buffer)
|
bufferPool.release(buffer)
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +111,6 @@ private[io] class UdpConnection(selectorRouter: ActorRef,
|
||||||
// Datagram channel either sends the whole message, or nothing
|
// Datagram channel either sends the whole message, or nothing
|
||||||
if (writtenBytes == 0) commander ! CommandFailed(send)
|
if (writtenBytes == 0) commander ! CommandFailed(send)
|
||||||
else if (send.wantsAck) commander ! send.ack
|
else if (send.wantsAck) commander ! send.ack
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
udpConn.bufferPool.release(buffer)
|
udpConn.bufferPool.release(buffer)
|
||||||
pendingSend = null
|
pendingSend = null
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ object UdpFF extends ExtensionKey[UdpFFExt] {
|
||||||
options: immutable.Traversable[SocketOption] = Nil) extends Command
|
options: immutable.Traversable[SocketOption] = Nil) extends Command
|
||||||
case object Unbind extends Command
|
case object Unbind extends Command
|
||||||
|
|
||||||
case object SimpleSender extends Command
|
case class SimpleSender(options: immutable.Traversable[SocketOption] = Nil) extends Command
|
||||||
|
|
||||||
case object StopReading extends Command
|
case object StopReading extends Command
|
||||||
case object ResumeReading extends Command
|
case object ResumeReading extends Command
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ import scala.collection.immutable
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.io.UdpFF.Received
|
import akka.io.UdpFF.Received
|
||||||
import akka.io.SelectionHandler.RegisterChannel
|
import akka.io.SelectionHandler.RegisterChannel
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
private[io] class UdpFFListener(selectorRouter: ActorRef,
|
private[io] class UdpFFListener(selectorRouter: ActorRef,
|
||||||
handler: ActorRef,
|
handler: ActorRef,
|
||||||
|
|
@ -34,6 +36,7 @@ private[io] class UdpFFListener(selectorRouter: ActorRef,
|
||||||
datagramChannel.configureBlocking(false)
|
datagramChannel.configureBlocking(false)
|
||||||
val socket = datagramChannel.socket
|
val socket = datagramChannel.socket
|
||||||
options.foreach(_.beforeDatagramBind(socket))
|
options.foreach(_.beforeDatagramBind(socket))
|
||||||
|
// FIXME: signal bind failures
|
||||||
socket.bind(endpoint) // will blow up the actor constructor if the bind fails
|
socket.bind(endpoint) // will blow up the actor constructor if the bind fails
|
||||||
datagramChannel
|
datagramChannel
|
||||||
}
|
}
|
||||||
|
|
@ -60,8 +63,7 @@ private[io] class UdpFFListener(selectorRouter: ActorRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
def doReceive(handler: ActorRef): Unit = {
|
def doReceive(handler: ActorRef): Unit = {
|
||||||
val buffer = bufferPool.acquire()
|
@tailrec def innerReceive(readsLeft: Int, buffer: ByteBuffer) {
|
||||||
try {
|
|
||||||
buffer.clear()
|
buffer.clear()
|
||||||
buffer.limit(DirectBufferSize)
|
buffer.limit(DirectBufferSize)
|
||||||
|
|
||||||
|
|
@ -69,11 +71,16 @@ private[io] class UdpFFListener(selectorRouter: ActorRef,
|
||||||
case sender: InetSocketAddress ⇒
|
case sender: InetSocketAddress ⇒
|
||||||
buffer.flip()
|
buffer.flip()
|
||||||
handler ! Received(ByteString(buffer), sender)
|
handler ! Received(ByteString(buffer), sender)
|
||||||
case _ ⇒ // Ignore
|
if (readsLeft > 0) innerReceive(readsLeft - 1, buffer)
|
||||||
|
case null ⇒ // null means no data was available
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val buffer = bufferPool.acquire()
|
||||||
|
try innerReceive(BatchReceiveLimit, buffer) finally {
|
||||||
|
bufferPool.release(buffer)
|
||||||
selector ! ReadInterest
|
selector ! ReadInterest
|
||||||
} finally bufferPool.release(buffer)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postStop() {
|
override def postStop() {
|
||||||
|
|
|
||||||
|
|
@ -44,17 +44,13 @@ import akka.io.UdpFF._
|
||||||
*/
|
*/
|
||||||
private[io] class UdpFFManager(udpFF: UdpFFExt) extends SelectorBasedManager(udpFF.settings, udpFF.settings.NrOfSelectors) {
|
private[io] class UdpFFManager(udpFF: UdpFFExt) extends SelectorBasedManager(udpFF.settings, udpFF.settings.NrOfSelectors) {
|
||||||
|
|
||||||
// FIXME: fix close overs
|
|
||||||
lazy val anonymousSender: ActorRef = context.actorOf(
|
|
||||||
props = Props(new UdpFFSender(udpFF, selectorPool)),
|
|
||||||
name = "simplesend")
|
|
||||||
|
|
||||||
def receive = workerForCommand {
|
def receive = workerForCommand {
|
||||||
case Bind(handler, endpoint, options) ⇒
|
case Bind(handler, endpoint, options) ⇒
|
||||||
val commander = sender
|
val commander = sender
|
||||||
Props(new UdpFFListener(selectorPool, handler, endpoint, commander, udpFF, options))
|
Props(new UdpFFListener(selectorPool, handler, endpoint, commander, udpFF, options))
|
||||||
} orElse {
|
case SimpleSender(options) ⇒
|
||||||
case SimpleSender ⇒ anonymousSender forward SimpleSender
|
val commander = sender
|
||||||
|
Props(new UdpFFSender(udpFF, options, commander))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,27 +7,34 @@ import akka.actor._
|
||||||
import java.nio.channels.DatagramChannel
|
import java.nio.channels.DatagramChannel
|
||||||
import akka.io.UdpFF._
|
import akka.io.UdpFF._
|
||||||
import akka.io.SelectionHandler.{ ChannelRegistered, RegisterChannel }
|
import akka.io.SelectionHandler.{ ChannelRegistered, RegisterChannel }
|
||||||
|
import scala.collection.immutable
|
||||||
|
import akka.io.Inet.SocketOption
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for TcpIncomingConnection and TcpOutgoingConnection.
|
* Base class for TcpIncomingConnection and TcpOutgoingConnection.
|
||||||
*/
|
*/
|
||||||
private[io] class UdpFFSender(val udpFF: UdpFFExt, val selector: ActorRef)
|
private[io] class UdpFFSender(val udpFF: UdpFFExt, options: immutable.Traversable[SocketOption], val commander: ActorRef)
|
||||||
extends Actor with ActorLogging with WithUdpFFSend {
|
extends Actor with ActorLogging with WithUdpFFSend {
|
||||||
|
|
||||||
|
def selector: ActorRef = context.parent
|
||||||
|
|
||||||
val channel = {
|
val channel = {
|
||||||
val datagramChannel = DatagramChannel.open
|
val datagramChannel = DatagramChannel.open
|
||||||
datagramChannel.configureBlocking(false)
|
datagramChannel.configureBlocking(false)
|
||||||
|
val socket = datagramChannel.socket
|
||||||
|
|
||||||
|
options foreach { o ⇒
|
||||||
|
o.beforeDatagramBind(socket)
|
||||||
|
}
|
||||||
|
|
||||||
datagramChannel
|
datagramChannel
|
||||||
}
|
}
|
||||||
selector ! RegisterChannel(channel, 0)
|
selector ! RegisterChannel(channel, 0)
|
||||||
|
|
||||||
def receive: Receive = {
|
def receive: Receive = {
|
||||||
case ChannelRegistered ⇒ context.become(simpleSendHandlers orElse sendHandlers, discardOld = true)
|
case ChannelRegistered ⇒
|
||||||
case _ ⇒ sender ! SimpleSendReady // FIXME: queueing here?
|
context.become(sendHandlers, discardOld = true)
|
||||||
}
|
commander ! SimpleSendReady
|
||||||
|
|
||||||
def simpleSendHandlers: Receive = {
|
|
||||||
case SimpleSender ⇒ sender ! SimpleSendReady
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postStop(): Unit = if (channel.isOpen) channel.close()
|
override def postStop(): Unit = if (channel.isOpen) channel.close()
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,9 @@ private[io] trait WithUdpFFSend {
|
||||||
me: Actor with ActorLogging ⇒
|
me: Actor with ActorLogging ⇒
|
||||||
|
|
||||||
var pendingSend: (Send, ActorRef) = null
|
var pendingSend: (Send, ActorRef) = null
|
||||||
|
// If send fails first, we allow a second go after selected writable, but no more. This flag signals that
|
||||||
|
// pending send was already tried once.
|
||||||
|
var retriedSend = false
|
||||||
def writePending = pendingSend ne null
|
def writePending = pendingSend ne null
|
||||||
|
|
||||||
def selector: ActorRef
|
def selector: ActorRef
|
||||||
|
|
@ -33,7 +36,7 @@ private[io] trait WithUdpFFSend {
|
||||||
|
|
||||||
case send: Send ⇒
|
case send: Send ⇒
|
||||||
pendingSend = (send, sender)
|
pendingSend = (send, sender)
|
||||||
selector ! WriteInterest
|
doSend()
|
||||||
|
|
||||||
case ChannelWritable ⇒ doSend()
|
case ChannelWritable ⇒ doSend()
|
||||||
|
|
||||||
|
|
@ -51,12 +54,19 @@ private[io] trait WithUdpFFSend {
|
||||||
if (TraceLogging) log.debug("Wrote {} bytes to channel", writtenBytes)
|
if (TraceLogging) log.debug("Wrote {} bytes to channel", writtenBytes)
|
||||||
|
|
||||||
// Datagram channel either sends the whole message, or nothing
|
// Datagram channel either sends the whole message, or nothing
|
||||||
if (writtenBytes == 0) commander ! CommandFailed(send)
|
if (writtenBytes == 0) {
|
||||||
else if (send.wantsAck) commander ! send.ack
|
if (retriedSend) {
|
||||||
|
commander ! CommandFailed(send)
|
||||||
|
retriedSend = false
|
||||||
|
pendingSend = null
|
||||||
|
} else {
|
||||||
|
selector ! WriteInterest
|
||||||
|
retriedSend = true
|
||||||
|
}
|
||||||
|
} else if (send.wantsAck) commander ! send.ack
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
udpFF.bufferPool.release(buffer)
|
udpFF.bufferPool.release(buffer)
|
||||||
pendingSend = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue