Skip to content

Commit a09e961

Browse files
committed
fixup! KTOR-1159 Add support for UDP sockets on native
1 parent 0a8a211 commit a09e961

File tree

2 files changed

+92
-39
lines changed

2 files changed

+92
-39
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.network.sockets
6+
7+
import io.ktor.network.selector.*
8+
import io.ktor.network.util.*
9+
import io.ktor.utils.io.core.*
10+
import io.ktor.utils.io.errors.*
11+
import kotlinx.cinterop.*
12+
import kotlinx.coroutines.*
13+
import kotlinx.coroutines.channels.*
14+
import kotlinx.coroutines.selects.*
15+
import kotlinx.coroutines.sync.*
16+
import platform.posix.*
17+
18+
internal class DatagramSendChannel(
19+
val descriptor: Int,
20+
val socket: DatagramSocketImpl
21+
) : SendChannel<Datagram> {
22+
@ExperimentalCoroutinesApi
23+
override val isClosedForSend: Boolean
24+
get() = socket.isClosed
25+
26+
@ExperimentalCoroutinesApi
27+
override val isFull: Boolean
28+
get() = if (isClosedForSend) false else lock.isLocked
29+
30+
private val lock = Mutex()
31+
32+
override fun close(cause: Throwable?): Boolean {
33+
// if (socket.isClosed) {
34+
// return false
35+
// }
36+
//
37+
// socket.close()
38+
return true
39+
}
40+
41+
42+
override fun offer(element: Datagram): Boolean {
43+
TODO("not implemented")
44+
}
45+
46+
override suspend fun send(element: Datagram) {
47+
lock.withLock {
48+
sendImpl(element)
49+
}
50+
}
51+
52+
private tailrec suspend fun sendImpl(
53+
datagram: Datagram,
54+
bytes: ByteArray = datagram.packet.readBytes()
55+
) {
56+
var bytesWritten: Int? = null
57+
bytes.usePinned { pinned ->
58+
datagram.address.address.nativeAddress { address, addressSize ->
59+
bytesWritten = sendto(
60+
descriptor,
61+
pinned.addressOf(0),
62+
bytes.size.convert(),
63+
0,
64+
address,
65+
addressSize
66+
).toInt()
67+
}
68+
}
69+
when (bytesWritten ?: error("bytesWritten cannot be null")) {
70+
0 -> throw IOException("Failed writing to closed socket")
71+
-1 -> {
72+
if (errno == EAGAIN) {
73+
socket.selector.select(socket.selectable, SelectInterest.WRITE)
74+
sendImpl(datagram, bytes)
75+
} else {
76+
throw PosixException.forErrno()
77+
}
78+
}
79+
}
80+
}
81+
82+
override val onSend: SelectClause2<Datagram, SendChannel<Datagram>>
83+
get() = TODO("[DatagramSendChannel] doesn't support [onSend] select clause")
84+
85+
@ExperimentalCoroutinesApi
86+
override fun invokeOnClose(handler: (cause: Throwable?) -> Unit) {
87+
TODO("[DatagramSendChannel] doesn't support [invokeOnClose] operation.")
88+
}
89+
}

ktor-network/posix/src/io/ktor/network/sockets/DatagramSocketImpl.kt

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ import kotlin.coroutines.*
1818

1919
internal class DatagramSocketImpl(
2020
private val descriptor: Int,
21-
private val selector: SelectorManager,
21+
val selector: SelectorManager,
2222
private val _localAddress: NetworkAddress,
2323
private val _remoteAddress: NetworkAddress?,
2424
parent: CoroutineContext = EmptyCoroutineContext
2525
) : BoundDatagramSocket, ConnectedDatagramSocket, Socket, CoroutineScope {
2626
private val _context: CompletableJob = Job(parent[Job])
27-
private val selectable: SelectableNative = SelectableNative(descriptor)
27+
val selectable: SelectableNative = SelectableNative(descriptor)
2828

2929
override val coroutineContext: CoroutineContext = parent + Dispatchers.Unconfined + _context
3030

@@ -36,7 +36,7 @@ internal class DatagramSocketImpl(
3636
override val remoteAddress: NetworkAddress
3737
get() = _remoteAddress!! // TODO: What should happen here?
3838

39-
private val sender = Channel<Datagram>()
39+
private val sender: SendChannel<Datagram> = DatagramSendChannel(descriptor, this)
4040

4141
private val receiver = produce<Datagram>(coroutineContext) {
4242
while (true) {
@@ -47,12 +47,6 @@ internal class DatagramSocketImpl(
4747

4848
init {
4949
makeShared()
50-
51-
launch {
52-
sender.consumeEach { datagram ->
53-
sendImpl(datagram)
54-
}
55-
}
5650
}
5751

5852
override val outgoing: SendChannel<Datagram>
@@ -66,36 +60,6 @@ internal class DatagramSocketImpl(
6660
override fun attachForWriting(channel: ByteChannel): ReaderJob =
6761
attachForWritingImpl(channel, descriptor, selectable, selector)
6862

69-
private tailrec suspend fun sendImpl(
70-
datagram: Datagram,
71-
bytes: ByteArray = datagram.packet.readBytes()
72-
) {
73-
var bytesWritten: Int? = null
74-
bytes.usePinned { pinned ->
75-
datagram.address.address.nativeAddress { address, addressSize ->
76-
bytesWritten = sendto(
77-
descriptor,
78-
pinned.addressOf(0),
79-
bytes.size.convert(),
80-
0,
81-
address,
82-
addressSize
83-
).toInt()
84-
}
85-
}
86-
when (bytesWritten ?: error("bytesWritten cannot be null")) {
87-
0 -> throw IOException("Failed writing to closed socket")
88-
-1 -> {
89-
if (errno == EAGAIN) {
90-
selector.select(selectable, SelectInterest.WRITE)
91-
sendImpl(datagram, bytes)
92-
} else {
93-
throw PosixException.forErrno()
94-
}
95-
}
96-
}
97-
}
98-
9963
private tailrec suspend fun receiveImpl(
10064
buffer: IoBuffer = DefaultDatagramByteBufferPool.borrow()
10165
): Datagram {

0 commit comments

Comments
 (0)