@@ -69,7 +69,17 @@ internal open class ConflatedBufferedChannel<E>(
69
69
return success(Unit )
70
70
}
71
71
72
- private fun trySendDropOldest (element : E ): ChannelResult <Unit > =
72
+ private val closeStarted = atomic(false )
73
+ private val trySendStarted = atomic(1L )
74
+ private val trySendCompleted = atomic(1L )
75
+
76
+ private fun trySendDropOldest (element : E ): ChannelResult <Unit > = try {
77
+ trySendStarted.incrementAndGet()
78
+ if (closeStarted.value) {
79
+ trySendCompleted.incrementAndGet()
80
+ while (! isClosedForSend) {}
81
+ trySendStarted.incrementAndGet()
82
+ }
73
83
sendImpl( // <-- this is an inline function
74
84
element = element,
75
85
// Put the element into the logical buffer in any case,
@@ -90,6 +100,9 @@ internal open class ConflatedBufferedChannel<E>(
90
100
// If the channel is closed, return the corresponding result.
91
101
onClosed = { closed(sendException) }
92
102
)
103
+ } finally {
104
+ trySendCompleted.incrementAndGet()
105
+ }
93
106
94
107
@Suppress(" UNCHECKED_CAST" )
95
108
override fun registerSelectForSend (select : SelectInstance <* >, element : Any? ) {
@@ -109,4 +122,26 @@ internal open class ConflatedBufferedChannel<E>(
109
122
}
110
123
111
124
override fun shouldSendSuspend () = false // never suspends
125
+
126
+ override fun closeOrCancelImpl (cause : Throwable ? , cancel : Boolean ): Boolean {
127
+ // Inform `trySend(..)` the the channel closing procedure has been started.
128
+ // All `trySend(..)`s that start after setting this flag, should wait
129
+ // until the channel is properly closed. Otherwise, a non-linearizable
130
+ // behaviour can be observed.
131
+ closeStarted.value = true
132
+ // It is critical to wait until all the started `trySend(..)`s
133
+ // complete their operation.
134
+ waitUntilStartedTrySendsComplete()
135
+ // Finally, close/cancel this channel.
136
+ return super .closeOrCancelImpl(cause, cancel)
137
+ }
138
+
139
+ private fun waitUntilStartedTrySendsComplete () {
140
+ while (true ) {
141
+ val started = trySendStarted.value
142
+ val completed = trySendCompleted.value
143
+ if (started != trySendStarted.value) continue
144
+ if (started == completed) break
145
+ }
146
+ }
112
147
}
0 commit comments