blob: 75e421c6e7112c07cdc3153f1e9bb59ffaaa52ff [file] [log] [blame]
Vsevolod Tolstopyatov6d1a6e32020-02-18 15:28:00 +03001/*
Aurimas Liutikasc8879d62021-05-12 21:56:16 +00002 * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Vsevolod Tolstopyatov6d1a6e32020-02-18 15:28:00 +03003 */
4
Roman Elizarov0950dfa2018-07-13 10:33:25 +03005package kotlinx.coroutines.channels
Roman Elizarov3aed4ee2017-03-06 12:21:05 +03006
SokolovaMaria0126dba2019-11-26 01:04:07 +03007import kotlinx.coroutines.*
Vsevolod Tolstopyatov69c15b32018-11-09 18:14:00 +03008import kotlinx.coroutines.internal.*
SokolovaMaria0126dba2019-11-26 01:04:07 +03009import kotlinx.coroutines.selects.*
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030010
11/**
12 * Channel that buffers at most one element and conflates all subsequent `send` and `offer` invocations,
13 * so that the receiver always gets the most recently sent element.
Roman Elizarov6862afc2020-03-04 16:17:31 +030014 * Back-to-send sent elements are _conflated_ -- only the most recently sent element is received,
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030015 * while previously sent elements **are lost**.
16 * Sender to this channel never suspends and [offer] always returns `true`.
17 *
Roman Elizarovf2a710a2017-07-21 18:33:59 +030018 * This channel is created by `Channel(Channel.CONFLATED)` factory function invocation.
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030019 */
Roman Elizarov8773a262020-10-12 19:09:48 +030020internal open class ConflatedChannel<E>(onUndeliveredElement: OnUndeliveredElement<E>?) : AbstractChannel<E>(onUndeliveredElement) {
SokolovaMaria0126dba2019-11-26 01:04:07 +030021 protected final override val isBufferAlwaysEmpty: Boolean get() = false
22 protected final override val isBufferEmpty: Boolean get() = value === EMPTY
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030023 protected final override val isBufferAlwaysFull: Boolean get() = false
24 protected final override val isBufferFull: Boolean get() = false
25
SokolovaMaria0126dba2019-11-26 01:04:07 +030026 override val isEmpty: Boolean get() = lock.withLock { isEmptyImpl }
27
28 private val lock = ReentrantLock()
29
30 private var value: Any? = EMPTY
31
SokolovaMaria0126dba2019-11-26 01:04:07 +030032 // result is `OFFER_SUCCESS | Closed`
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030033 protected override fun offerInternal(element: E): Any {
SokolovaMaria0126dba2019-11-26 01:04:07 +030034 var receive: ReceiveOrClosed<E>? = null
35 lock.withLock {
36 closedForSend?.let { return it }
37 // if there is no element written in buffer
38 if (value === EMPTY) {
39 // check for receivers that were waiting on the empty buffer
40 loop@ while(true) {
41 receive = takeFirstReceiveOrPeekClosed() ?: break@loop // break when no receivers queued
42 if (receive is Closed) {
43 return receive!!
Roman Elizarove6e8ce82017-06-05 17:04:39 +030044 }
SokolovaMaria0126dba2019-11-26 01:04:07 +030045 val token = receive!!.tryResumeReceive(element, null)
46 if (token != null) {
47 assert { token === RESUME_TOKEN }
48 return@withLock
49 }
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030050 }
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030051 }
Roman Elizarov8773a262020-10-12 19:09:48 +030052 updateValueLocked(element)?.let { throw it }
SokolovaMaria0126dba2019-11-26 01:04:07 +030053 return OFFER_SUCCESS
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030054 }
SokolovaMaria0126dba2019-11-26 01:04:07 +030055 // breaks here if offer meets receiver
56 receive!!.completeResumeReceive(element)
57 return receive!!.offerResult
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030058 }
59
SokolovaMaria0126dba2019-11-26 01:04:07 +030060 // result is `ALREADY_SELECTED | OFFER_SUCCESS | Closed`
Roman Elizarov3aed4ee2017-03-06 12:21:05 +030061 protected override fun offerSelectInternal(element: E, select: SelectInstance<*>): Any {
SokolovaMaria0126dba2019-11-26 01:04:07 +030062 var receive: ReceiveOrClosed<E>? = null
63 lock.withLock {
64 closedForSend?.let { return it }
65 if (value === EMPTY) {
66 loop@ while(true) {
67 val offerOp = describeTryOffer(element)
68 val failure = select.performAtomicTrySelect(offerOp)
69 when {
70 failure == null -> { // offered successfully
71 receive = offerOp.result
72 return@withLock
73 }
74 failure === OFFER_FAILED -> break@loop // cannot offer -> Ok to queue to buffer
75 failure === RETRY_ATOMIC -> {} // retry
76 failure === ALREADY_SELECTED || failure is Closed<*> -> return failure
77 else -> error("performAtomicTrySelect(describeTryOffer) returned $failure")
78 }
79 }
80 }
81 // try to select sending this element to buffer
82 if (!select.trySelect()) {
83 return ALREADY_SELECTED
84 }
Roman Elizarov8773a262020-10-12 19:09:48 +030085 updateValueLocked(element)?.let { throw it }
SokolovaMaria0126dba2019-11-26 01:04:07 +030086 return OFFER_SUCCESS
87 }
88 // breaks here if offer meets receiver
89 receive!!.completeResumeReceive(element)
90 return receive!!.offerResult
91 }
92
93 // result is `E | POLL_FAILED | Closed`
94 protected override fun pollInternal(): Any? {
95 var result: Any? = null
96 lock.withLock {
97 if (value === EMPTY) return closedForSend ?: POLL_FAILED
98 result = value
99 value = EMPTY
100 }
101 return result
102 }
103
104 // result is `E | POLL_FAILED | Closed`
105 protected override fun pollSelectInternal(select: SelectInstance<*>): Any? {
106 var result: Any? = null
107 lock.withLock {
108 if (value === EMPTY) return closedForSend ?: POLL_FAILED
109 if (!select.trySelect())
110 return ALREADY_SELECTED
111 result = value
112 value = EMPTY
113 }
114 return result
115 }
116
117 protected override fun onCancelIdempotent(wasClosed: Boolean) {
Roman Elizarov8773a262020-10-12 19:09:48 +0300118 var undeliveredElementException: UndeliveredElementException? = null // resource cancel exception
119 lock.withLock {
120 undeliveredElementException = updateValueLocked(EMPTY)
Roman Elizarov3aed4ee2017-03-06 12:21:05 +0300121 }
SokolovaMaria0126dba2019-11-26 01:04:07 +0300122 super.onCancelIdempotent(wasClosed)
Aurimas Liutikasc8879d62021-05-12 21:56:16 +0000123 undeliveredElementException?.let { throw it } // throw exception at the end if there was one
Roman Elizarov8773a262020-10-12 19:09:48 +0300124 }
125
126 private fun updateValueLocked(element: Any?): UndeliveredElementException? {
127 val old = value
128 val undeliveredElementException = if (old === EMPTY) null else
129 onUndeliveredElement?.callUndeliveredElementCatchingException(old as E)
130 value = element
131 return undeliveredElementException
Roman Elizarov3aed4ee2017-03-06 12:21:05 +0300132 }
SokolovaMaria0126dba2019-11-26 01:04:07 +0300133
134 override fun enqueueReceiveInternal(receive: Receive<E>): Boolean = lock.withLock {
135 super.enqueueReceiveInternal(receive)
136 }
137
138 // ------ debug ------
139
140 override val bufferDebugString: String
141 get() = "(value=$value)"
Vsevolod Tolstopyatov6d1a6e32020-02-18 15:28:00 +0300142}