Skip to content

Commit dedd448

Browse files
committed
Deprecate SendChannel.offer and replace its usages along the code base
1 parent 80862a5 commit dedd448

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+180
-192
lines changed

kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ internal abstract class AbstractSendChannel<E>(
137137
}
138138

139139
override fun offer(element: E): Boolean {
140+
// Temporary migration for offer users who rely on onUndeliveredElement
140141
try {
141142
return super.offer(element)
142143
} catch (e: Throwable) {

kotlinx-coroutines-core/common/src/channels/Channel.kt

+34-26
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import kotlin.jvm.*
2424
public interface SendChannel<in E> {
2525
/**
2626
* Returns `true` if this channel was closed by an invocation of [close]. This means that
27-
* calling [send] or [offer] will result in an exception.
27+
* calling [send] will result in an exception.
2828
*
2929
* **Note: This is an experimental api.** This property may change its semantics and/or name in the future.
3030
*/
@@ -51,7 +51,7 @@ public interface SendChannel<in E> {
5151
* Use [yield] or [CoroutineScope.isActive] to periodically check for cancellation in tight loops if needed.
5252
*
5353
* This function can be used in [select] invocations with the [onSend] clause.
54-
* Use [offer] to try sending to this channel without waiting.
54+
* Use [trySend] to try sending to this channel without waiting.
5555
*/
5656
public suspend fun send(element: E)
5757

@@ -64,23 +64,6 @@ public interface SendChannel<in E> {
6464
*/
6565
public val onSend: SelectClause2<E, SendChannel<E>>
6666

67-
/**
68-
* Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions,
69-
* and returns `true`. Otherwise, just returns `false`. This is a synchronous variant of [send] which backs off
70-
* in situations when `send` suspends.
71-
*
72-
* Throws an exception if the channel [is closed for `send`][isClosedForSend] (see [close] for details).
73-
*
74-
* When `offer` call returns `false` it guarantees that the element was not delivered to the consumer and it
75-
* it does not call `onUndeliveredElement` that was installed for this channel. If the channel was closed,
76-
* then it calls `onUndeliveredElement` before throwing an exception.
77-
* See "Undelivered elements" section in [Channel] documentation for details on handling undelivered elements.
78-
*/
79-
public fun offer(element: E): Boolean {
80-
val result = trySend(element)
81-
if (result.isSuccess) return true
82-
throw recoverStackTrace(result.exceptionOrNull() ?: return false)
83-
}
8467

8568
/**
8669
* Immediately adds the specified [element] to this channel, if this doesn't violate its capacity restrictions,
@@ -103,7 +86,7 @@ public interface SendChannel<in E> {
10386
* on the side of [ReceiveChannel] starts returning `true` only after all previously sent elements
10487
* are received.
10588
*
106-
* A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send] or [offer]
89+
* A channel that was closed without a [cause] throws a [ClosedSendChannelException] on attempts to [send]
10790
* and [ClosedReceiveChannelException] on attempts to [receive][ReceiveChannel.receive].
10891
* A channel that was closed with non-null [cause] is called a _failed_ channel. Attempts to send or
10992
* receive on a failed channel throw the specified [cause] exception.
@@ -125,7 +108,7 @@ public interface SendChannel<in E> {
125108
* ```
126109
* val events = Channel(UNLIMITED)
127110
* callbackBasedApi.registerCallback { event ->
128-
* events.offer(event)
111+
* events.trySend(event)
129112
* }
130113
*
131114
* val uiUpdater = launch(Dispatchers.Main, parent = UILifecycle) {
@@ -146,6 +129,33 @@ public interface SendChannel<in E> {
146129
*/
147130
@ExperimentalCoroutinesApi
148131
public fun invokeOnClose(handler: (cause: Throwable?) -> Unit)
132+
133+
/**
134+
* **Deprecated** offer method.
135+
*
136+
* This method was deprecated in the favour of [trySend].
137+
* It has proven itself as the most error-prone method in Channel API:
138+
*
139+
* * `Boolean` return type creates the false sense of security, implying that `false`
140+
* is returned instead of throwing an exception.
141+
* * It was used mostly from non-suspending APIs where CancellationException triggered
142+
* internal failures in the application (the most common source of bugs).
143+
* * Due to signature and explicit `if (ch.offer(...))` checks it was easy to
144+
* oversee such error during code review.
145+
* * Its name was not aligned with the rest of the API and tried to mimic Java's queue instead.
146+
*
147+
* See https://github.com/Kotlin/kotlinx.coroutines/issues/974 for more context.
148+
*/
149+
@Deprecated(
150+
level = DeprecationLevel.WARNING,
151+
message = "Deprecated in the favour of 'trySend' method",
152+
replaceWith = ReplaceWith("trySend(element).isSuccess")
153+
)
154+
public fun offer(element: E): Boolean {
155+
val result = trySend(element)
156+
if (result.isSuccess) return true
157+
throw recoverStackTrace(result.exceptionOrNull() ?: return false)
158+
}
149159
}
150160

151161
/**
@@ -544,14 +554,14 @@ public interface ChannelIterator<out E> {
544554
*
545555
* * When `capacity` is [Channel.UNLIMITED] &mdash; it creates a channel with effectively unlimited buffer.
546556
* This channel has a linked-list buffer of unlimited capacity (limited only by available memory).
547-
* [Sending][send] to this channel never suspends, and [offer] always returns `true`.
557+
* [Sending][send] to this channel never suspends, and [trySend] always succeeds.
548558
*
549559
* * When `capacity` is [Channel.CONFLATED] &mdash; it creates a _conflated_ channel
550-
* This channel buffers at most one element and conflates all subsequent `send` and `offer` invocations,
560+
* This channel buffers at most one element and conflates all subsequent `send` and `trySend` invocations,
551561
* so that the receiver always gets the last element sent.
552562
* Back-to-back sent elements are conflated &mdash; only the last sent element is received,
553563
* while previously sent elements **are lost**.
554-
* [Sending][send] to this channel never suspends, and [offer] always returns `true`.
564+
* [Sending][send] to this channel never suspends, and [trySend] always succeeds.
555565
*
556566
* * When `capacity` is positive but less than [UNLIMITED] &mdash; it creates an array-based channel with the specified capacity.
557567
* This channel has an array buffer of a fixed `capacity`.
@@ -598,8 +608,6 @@ public interface ChannelIterator<out E> {
598608
*
599609
* * When [send][SendChannel.send] operation throws an exception because it was cancelled before it had a chance to actually
600610
* send the element or because the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
601-
* * When [offer][SendChannel.offer] operation throws an exception when
602-
* the channel was [closed][SendChannel.close] or [cancelled][ReceiveChannel.cancel].
603611
* * When [receive][ReceiveChannel.receive], [receiveOrNull][ReceiveChannel.receiveOrNull], or [hasNext][ChannelIterator.hasNext]
604612
* operation throws an exception when it had retrieved the element from the
605613
* channel but was cancelled before the code following the receive call resumed.

kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import kotlin.jvm.*
1717
* Back-to-send sent elements are _conflated_ -- only the the most recently sent value is received,
1818
* while previously sent elements **are lost**.
1919
* Every subscriber immediately receives the most recently sent element.
20-
* Sender to this broadcast channel never suspends and [offer] always returns `true`.
20+
* Sender to this broadcast channel never suspends and [trySend] always succeeds.
2121
*
2222
* A secondary constructor can be used to create an instance of this class that already holds a value.
2323
* This channel is also created by `BroadcastChannel(Channel.CONFLATED)` factory function invocation.

kotlinx-coroutines-core/common/src/channels/ConflatedChannel.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ import kotlinx.coroutines.internal.*
99
import kotlinx.coroutines.selects.*
1010

1111
/**
12-
* Channel that buffers at most one element and conflates all subsequent `send` and `offer` invocations,
12+
* Channel that buffers at most one element and conflates all subsequent `send` and `trySend` invocations,
1313
* so that the receiver always gets the most recently sent element.
1414
* Back-to-send sent elements are _conflated_ -- only the most recently sent element is received,
1515
* while previously sent elements **are lost**.
16-
* Sender to this channel never suspends and [offer] always returns `true`.
16+
* Sender to this channel never suspends and [trySend] always succeeds.
1717
*
1818
* This channel is created by `Channel(Channel.CONFLATED)` factory function invocation.
1919
*/

kotlinx-coroutines-core/common/src/channels/LinkedListChannel.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import kotlinx.coroutines.selects.*
99

1010
/**
1111
* Channel with linked-list buffer of a unlimited capacity (limited only by available memory).
12-
* Sender to this channel never suspends and [offer] always returns `true`.
12+
* Sender to this channel never suspends and [trySend] always succeeds.
1313
*
1414
* This channel is created by `Channel(Channel.UNLIMITED)` factory function invocation.
1515
*

kotlinx-coroutines-core/common/src/flow/SharedFlow.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ import kotlin.native.concurrent.*
9292
*
9393
* To migrate [BroadcastChannel] usage to [SharedFlow], start by replacing usages of the `BroadcastChannel(capacity)`
9494
* constructor with `MutableSharedFlow(0, extraBufferCapacity=capacity)` (broadcast channel does not replay
95-
* values to new subscribers). Replace [send][BroadcastChannel.send] and [offer][BroadcastChannel.offer] calls
95+
* values to new subscribers). Replace [send][BroadcastChannel.send] and [trySend][BroadcastChannel.trySend] calls
9696
* with [emit][MutableStateFlow.emit] and [tryEmit][MutableStateFlow.tryEmit], and convert subscribers' code to flow operators.
9797
*
9898
* ### Concurrency

kotlinx-coroutines-core/common/src/flow/StateFlow.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ import kotlin.native.concurrent.*
107107
*
108108
* To migrate [ConflatedBroadcastChannel] usage to [StateFlow], start by replacing usages of the `ConflatedBroadcastChannel()`
109109
* constructor with `MutableStateFlow(initialValue)`, using `null` as an initial value if you don't have one.
110-
* Replace [send][ConflatedBroadcastChannel.send] and [offer][ConflatedBroadcastChannel.offer] calls
110+
* Replace [send][ConflatedBroadcastChannel.send] and [trySend][ConflatedBroadcastChannel.trySend] calls
111111
* with updates to the state flow's [MutableStateFlow.value], and convert subscribers' code to flow operators.
112112
* You can use the [filterNotNull] operator to mimic behavior of a `ConflatedBroadcastChannel` without initial value.
113113
*

kotlinx-coroutines-core/common/test/channels/ArrayChannelTest.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ class ArrayChannelTest : TestBase() {
8686
}
8787

8888
@Test
89-
fun testOfferAndPoll() = runTest {
89+
fun testTryOp() = runTest {
9090
val q = Channel<Int>(1)
91-
assertTrue(q.offer(1))
91+
assertTrue(q.trySend(1).isSuccess)
9292
expect(1)
9393
launch {
9494
expect(3)
@@ -106,11 +106,11 @@ class ArrayChannelTest : TestBase() {
106106
expect(2)
107107
yield()
108108
expect(6)
109-
assertTrue(q.offer(2))
109+
assertTrue(q.trySend(2).isSuccess)
110110
expect(7)
111-
assertTrue(q.offer(3))
111+
assertTrue(q.trySend(3).isSuccess)
112112
expect(8)
113-
assertFalse(q.offer(4))
113+
assertFalse(q.trySend(4).isSuccess)
114114
yield()
115115
finish(12)
116116
}
@@ -157,7 +157,7 @@ class ArrayChannelTest : TestBase() {
157157
val capacity = 42
158158
val channel = Channel<Int>(capacity)
159159
repeat(4) {
160-
channel.offer(-1)
160+
channel.trySend(-1)
161161
}
162162
repeat(4) {
163163
channel.receiveCatching().getOrNull()

kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt

+11-12
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class BasicOperationsTest : TestBase() {
1515
}
1616

1717
@Test
18-
fun testOfferAfterClose() = runTest {
19-
TestChannelKind.values().forEach { kind -> testOffer(kind) }
18+
fun testTrySendAfterClose() = runTest {
19+
TestChannelKind.values().forEach { kind -> testTrySend(kind) }
2020
}
2121

2222
@Test
@@ -39,7 +39,7 @@ class BasicOperationsTest : TestBase() {
3939
}
4040
}
4141
expect(1)
42-
channel.offer(42)
42+
channel.trySend(42)
4343
expect(2)
4444
channel.close(AssertionError())
4545
finish(4)
@@ -131,22 +131,21 @@ class BasicOperationsTest : TestBase() {
131131
finish(6)
132132
}
133133

134-
private suspend fun testOffer(kind: TestChannelKind) = coroutineScope {
134+
private suspend fun testTrySend(kind: TestChannelKind) = coroutineScope {
135135
val channel = kind.create<Int>()
136136
val d = async { channel.send(42) }
137137
yield()
138138
channel.close()
139139

140140
assertTrue(channel.isClosedForSend)
141-
try {
142-
channel.offer(2)
143-
fail()
144-
} catch (e: ClosedSendChannelException) {
145-
if (!kind.isConflated) {
146-
assertEquals(42, channel.receive())
141+
channel.trySend(2)
142+
.onSuccess { expectUnreached() }
143+
.onFailure {
144+
assertTrue { it is ClosedSendChannelException}
145+
if (!kind.isConflated) {
146+
assertEquals(42, channel.receive())
147+
}
147148
}
148-
}
149-
150149
d.await()
151150
}
152151

kotlinx-coroutines-core/common/test/channels/ChannelBufferOverflowTest.kt

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ class ChannelBufferOverflowTest : TestBase() {
1111
@Test
1212
fun testDropLatest() = runTest {
1313
val c = Channel<Int>(2, BufferOverflow.DROP_LATEST)
14-
assertTrue(c.offer(1))
15-
assertTrue(c.offer(2))
16-
assertTrue(c.offer(3)) // overflows, dropped
14+
assertTrue(c.trySend(1).isSuccess)
15+
assertTrue(c.trySend(2).isSuccess)
16+
assertTrue(c.trySend(3).isSuccess) // overflows, dropped
1717
c.send(4) // overflows dropped
1818
assertEquals(1, c.receive())
19-
assertTrue(c.offer(5))
20-
assertTrue(c.offer(6)) // overflows, dropped
19+
assertTrue(c.trySend(5).isSuccess)
20+
assertTrue(c.trySend(6).isSuccess) // overflows, dropped
2121
assertEquals(2, c.receive())
2222
assertEquals(5, c.receive())
2323
assertEquals(null, c.poll())
@@ -26,15 +26,15 @@ class ChannelBufferOverflowTest : TestBase() {
2626
@Test
2727
fun testDropOldest() = runTest {
2828
val c = Channel<Int>(2, BufferOverflow.DROP_OLDEST)
29-
assertTrue(c.offer(1))
30-
assertTrue(c.offer(2))
31-
assertTrue(c.offer(3)) // overflows, keeps 2, 3
29+
assertTrue(c.trySend(1).isSuccess)
30+
assertTrue(c.trySend(2).isSuccess)
31+
assertTrue(c.trySend(3).isSuccess) // overflows, keeps 2, 3
3232
c.send(4) // overflows, keeps 3, 4
3333
assertEquals(3, c.receive())
34-
assertTrue(c.offer(5))
35-
assertTrue(c.offer(6)) // overflows, keeps 5, 6
34+
assertTrue(c.trySend(5).isSuccess)
35+
assertTrue(c.trySend(6).isSuccess) // overflows, keeps 5, 6
3636
assertEquals(5, c.receive())
3737
assertEquals(6, c.receive())
3838
assertEquals(null, c.poll())
3939
}
40-
}
40+
}

kotlinx-coroutines-core/common/test/channels/ConflatedChannelTest.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ open class ConflatedChannelTest : TestBase() {
1515
fun testBasicConflationOfferPoll() {
1616
val q = createConflatedChannel<Int>()
1717
assertNull(q.poll())
18-
assertTrue(q.offer(1))
19-
assertTrue(q.offer(2))
20-
assertTrue(q.offer(3))
18+
assertTrue(q.trySend(1).isSuccess)
19+
assertTrue(q.trySend(2).isSuccess)
20+
assertTrue(q.trySend(3).isSuccess)
2121
assertEquals(3, q.poll())
2222
assertNull(q.poll())
2323
}

kotlinx-coroutines-core/common/test/channels/LinkedListChannelTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class LinkedListChannelTest : TestBase() {
1212
fun testBasic() = runTest {
1313
val c = Channel<Int>(Channel.UNLIMITED)
1414
c.send(1)
15-
check(c.offer(2))
15+
assertTrue(c.trySend(2).isSuccess)
1616
c.send(3)
1717
check(c.close())
1818
check(!c.close())

kotlinx-coroutines-core/common/test/channels/RendezvousChannelTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class RendezvousChannelTest : TestBase() {
8282
@Test
8383
fun testOfferAndPool() = runTest {
8484
val q = Channel<Int>(Channel.RENDEZVOUS)
85-
assertFalse(q.offer(1))
85+
assertFalse(q.trySend(1).isSuccess)
8686
expect(1)
8787
launch {
8888
expect(3)
@@ -99,7 +99,7 @@ class RendezvousChannelTest : TestBase() {
9999
expect(2)
100100
yield()
101101
expect(5)
102-
assertTrue(q.offer(2))
102+
assertTrue(q.trySend(2).isSuccess)
103103
expect(6)
104104
yield()
105105
expect(8)

kotlinx-coroutines-core/common/test/flow/channels/ChannelBuildersFlowTest.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -166,15 +166,15 @@ class ChannelBuildersFlowTest : TestBase() {
166166

167167
var expected = 0
168168
launch {
169-
assertTrue(channel.offer(1)) // Handed to the coroutine
170-
assertTrue(channel.offer(2)) // Buffered
171-
assertFalse(channel.offer(3)) // Failed to offer
169+
assertTrue(channel.trySend(1).isSuccess) // Handed to the coroutine
170+
assertTrue(channel.trySend(2).isSuccess) // Buffered
171+
assertFalse(channel.trySend(3).isSuccess) // Failed to offer
172172
channel.send(3)
173173
yield()
174174
assertEquals(1, expected)
175-
assertTrue(channel.offer(4)) // Handed to the coroutine
176-
assertTrue(channel.offer(5)) // Buffered
177-
assertFalse(channel.offer(6)) // Failed to offer
175+
assertTrue(channel.trySend(4).isSuccess) // Handed to the coroutine
176+
assertTrue(channel.trySend(5).isSuccess) // Buffered
177+
assertFalse(channel.trySend(6).isSuccess) // Failed to offer
178178
channel.send(6)
179179
assertEquals(2, expected)
180180
}

0 commit comments

Comments
 (0)