@@ -812,16 +812,24 @@ internal open class BufferedChannel<E>(
812
812
)
813
813
}
814
814
815
- // TODO: method name, documentation, implementation.
816
- protected fun dropFirstElementIfNeeded (s : Long ) {
815
+ /* *
816
+ * Extracts the first element from this channel until the cell with the specified
817
+ * index is moved to the logical buffer. This is a key procedure for the _conflated_
818
+ * channel implementation, see [ConflatedBufferedChannel] with the [BufferOverflow.DROP_OLDEST]
819
+ * strategy on buffer overflowing.
820
+ */
821
+ protected fun dropFirstElementUntilTheSpecifiedCellIsInTheBuffer (globalCellIndex : Long ) {
822
+ assert { isConflatedDropOldest }
817
823
// Read the segment reference before the counter increment;
818
824
// it is crucial to be able to find the required segment later.
819
825
var segment = receiveSegment.value
820
826
while (true ) {
821
- // Atomically increments the ` receivers` counter
822
- // and obtain the value right before the increment .
827
+ // Read the receivers counter to check whether the specified cell is already in the buffer
828
+ // or should be moved to the buffer in a short time, due to the already started `receive()` .
823
829
val r = this .receivers.value
824
- if (s < max(r + capacity, bufferEndCounter)) return
830
+ if (globalCellIndex < max(r + capacity, bufferEndCounter)) return
831
+ // The cell is outside the buffer. Try to extract the first element
832
+ // if the `receivers` counter has not been changed.
825
833
if (! this .receivers.compareAndSet(r, r + 1 )) continue
826
834
// Count the required segment id and the cell index in it.
827
835
val id = r / SEGMENT_SIZE
@@ -831,25 +839,26 @@ internal open class BufferedChannel<E>(
831
839
if (segment.id != id) {
832
840
// Find the required segment, restarting the operation if it has not been found.
833
841
segment = findSegmentReceive(id, segment) ? :
834
- // The required segment is not found. It is possible that the channel is already
842
+ // The required segment has not been found. It is possible that the channel is already
835
843
// closed for receiving, so the linked list of segments is closed as well.
836
- // In the latter case, the operation fails with the corresponding check at the beginning.
844
+ // In the latter case, the operation will finish eventually after incrementing
845
+ // the `receivers` counter sufficient times. Note that it is impossible to check
846
+ // whether this channel is closed for receiving (we do this in `receive`),
847
+ // as it may call this function when helping to complete closing the channel.
837
848
continue
838
849
}
839
850
// Update the cell according to the cell life-cycle.
840
851
val updCellResult = updateCellReceive(segment, i, r, null )
841
852
when {
842
853
updCellResult == = FAILED -> {
843
- // The cell is poisoned.
844
- // Restart from the beginning in this case.
854
+ // The cell is poisoned; restart from the beginning.
845
855
// To avoid memory leaks, we also need to reset
846
- // the `prev` pointer of the working segment.
856
+ // the `prev` pointer of t he working segment.
847
857
if (r < sendersCounter) segment.cleanPrev()
848
858
}
849
859
else -> { // element
850
- // Either a buffered element was retrieved from the cell
851
- // or a rendezvous with a waiting sender has happened.
852
- // Clean the reference to the previous segment before finishing.
860
+ // A buffered element was retrieved from the cell.
861
+ // Clean the reference to the previous segment.
853
862
segment.cleanPrev()
854
863
@Suppress(" UNCHECKED_CAST" )
855
864
onUndeliveredElement?.invoke(updCellResult as E )
@@ -1961,8 +1970,20 @@ internal open class BufferedChannel<E>(
1961
1970
// Close the linked list for further segment addition,
1962
1971
// obtaining the last segment in the data structure.
1963
1972
val lastSegment = closeLinkedList()
1964
- // TODO
1965
- if (isConflatedDropOldest) xxx(lastSegment)
1973
+ // In the conflated channel implementation (with the DROP_OLDEST
1974
+ // elements conflation strategy), it is critical to mark all empty
1975
+ // cells as closed to prevent in-progress `send(e)`-s, which have not
1976
+ // put their elements yet, completions after this channel is closed.
1977
+ // Otherwise, it is possible for a `send(e)` to put an element when
1978
+ // the buffer is already full, while a concurrent receiver may extract
1979
+ // the oldest element. When the channel is not closed, we can linearize
1980
+ // this `receive()` before the `send(e)`, but after the channel is closed,
1981
+ // `send(e)` must fails. Marking all unprocessed cells as `CLOSED` solves the issue.
1982
+ if (isConflatedDropOldest) {
1983
+ val lastBufferedCellGlobalIndex = markAllEmptyCellsAsClosed(lastSegment)
1984
+ if (lastBufferedCellGlobalIndex != - 1L )
1985
+ dropFirstElementUntilTheSpecifiedCellIsInTheBuffer(lastBufferedCellGlobalIndex)
1986
+ }
1966
1987
// Resume waiting `receive()` requests,
1967
1988
// informing them that the channel is closed.
1968
1989
cancelSuspendedReceiveRequests(lastSegment, sendersCur)
@@ -1971,10 +1992,20 @@ internal open class BufferedChannel<E>(
1971
1992
return lastSegment
1972
1993
}
1973
1994
1974
- private fun xxx (lastSegment : ChannelSegment <E >) {
1995
+ /* *
1996
+ * This function marks all empty cells, in the `null` and [IN_BUFFER] state,
1997
+ * as closed. Notably, it processes the cells from right to left, and finishes
1998
+ * immediately when the processing cell is already covered by `receive()` or
1999
+ * contains a buffered elements ([BUFFERED] state).
2000
+ *
2001
+ * This function returns the global index of the last buffered element,
2002
+ * or `-1` if this channel does not contain buffered elements.
2003
+ */
2004
+ private fun markAllEmptyCellsAsClosed (lastSegment : ChannelSegment <E >): Long {
1975
2005
var segment = lastSegment
1976
- traverse@ while (true ) {
2006
+ while (true ) {
1977
2007
for (index in SEGMENT_SIZE - 1 downTo 0 ) {
2008
+ if (segment.id * SEGMENT_SIZE + index < receiversCounter) return - 1
1978
2009
cell_update@while (true ) {
1979
2010
val state = segment.getState(index)
1980
2011
when {
@@ -1984,15 +2015,12 @@ internal open class BufferedChannel<E>(
1984
2015
break @cell_update
1985
2016
}
1986
2017
}
1987
- state == = BUFFERED -> {
1988
- dropFirstElementIfNeeded(segment.id * SEGMENT_SIZE + index)
1989
- break @traverse
1990
- }
2018
+ state == = BUFFERED -> return segment.id * SEGMENT_SIZE + index
1991
2019
else -> break @cell_update
1992
2020
}
1993
2021
}
1994
2022
}
1995
- segment = segment.prev ? : break
2023
+ segment = segment.prev ? : return - 1
1996
2024
}
1997
2025
}
1998
2026
0 commit comments