mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-08 22:54:46 +08:00
LockFreeLinkedList
This commit is contained in:
parent
4b28f00430
commit
a1d3cf0fd9
@ -6,67 +6,104 @@ import kotlinx.atomicfu.AtomicRef
|
|||||||
import kotlinx.atomicfu.atomic
|
import kotlinx.atomicfu.atomic
|
||||||
import net.mamoe.mirai.utils.Node.Companion.equals
|
import net.mamoe.mirai.utils.Node.Companion.equals
|
||||||
|
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
inline fun <E> lockFreeLinkedListOf(vararg elements: E): LockFreeLinkedList<E> = LockFreeLinkedList<E>().apply {
|
|
||||||
addAll(elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
|
||||||
inline fun <E> lockFreeLinkedListOf(): LockFreeLinkedList<E> = LockFreeLinkedList<E>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of lock-free LinkedList.
|
* Implementation of lock-free LinkedList.
|
||||||
*
|
*
|
||||||
* Modifying can be performed concurrently.
|
* Modifying can be performed concurrently.
|
||||||
* Iterating concurrency is guaranteed. // TODO: 2019/12/13 ADD MORE
|
* Iterating concurrency is guaranteed.
|
||||||
*/
|
*/
|
||||||
@MiraiExperimentalAPI
|
class LockFreeLinkedList<E> {
|
||||||
class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
|
|
||||||
private val tail: Tail<E> = Tail()
|
private val tail: Tail<E> = Tail()
|
||||||
|
|
||||||
private val head: Head<E> = Head(tail)
|
private val head: Head<E> = Head(tail)
|
||||||
|
|
||||||
override fun add(element: E): Boolean {
|
fun add(element: E) {
|
||||||
val node = element.asNode(tail)
|
val node = element.asNode(tail)
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
val tail = head.iterateBeforeFirst { it === tail } // find the last node.
|
val tail = head.iterateBeforeFirst { it === tail } // find the last node.
|
||||||
if (tail.nextNodeRef.compareAndSet(this.tail, node)) { // ensure the last node is the last node
|
if (tail.nextNodeRef.compareAndSet(this.tail, node)) { // ensure the last node is the last node
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun getLinkStucture(): String = buildString {
|
override fun toString(): String = "[" + buildString {
|
||||||
|
this@LockFreeLinkedList.forEach {
|
||||||
|
append(it)
|
||||||
|
append(", ")
|
||||||
|
}
|
||||||
|
}.dropLast(2) + "]"
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
internal fun getLinkStructure(): String = buildString {
|
||||||
head.childIterateReturnsLastSatisfying<Node<*>>({
|
head.childIterateReturnsLastSatisfying<Node<*>>({
|
||||||
append(it.toString())
|
append(it.toString())
|
||||||
append("->")
|
append("->")
|
||||||
it.nextNode
|
it.nextNode
|
||||||
}, { it !is Tail })
|
}, { it !is Tail })
|
||||||
}.let {
|
}.dropLast(2)
|
||||||
if (it.lastIndex > 0) {
|
|
||||||
it.substring(0..it.lastIndex - 2)
|
|
||||||
} else it
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun remove(element: E): Boolean {
|
fun remove(element: E): Boolean {
|
||||||
while (true) {
|
while (true) {
|
||||||
val before = head.iterateBeforeNodeValue(element)
|
val before = head.iterateBeforeNodeValue(element)
|
||||||
val toRemove = before.nextNode
|
val toRemove = before.nextNode
|
||||||
val next = toRemove.nextNode
|
|
||||||
if (toRemove === tail) {
|
if (toRemove === tail) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if (toRemove.nodeValue === null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
toRemove.nodeValue = null // logically remove: all the operations will recognize this node invalid
|
||||||
|
|
||||||
if (before.nextNodeRef.compareAndSet(toRemove, next)) {
|
var next: Node<E> = toRemove.nextNode
|
||||||
|
while (next !== tail && next.nodeValue === null) {
|
||||||
|
next = next.nextNode
|
||||||
|
}
|
||||||
|
if (before.nextNodeRef.compareAndSet(toRemove, next)) {// physically remove: try to fix the link
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val size: Int get() = head.countChildIterate<Node<E>>({ it.nextNodeRef.value }, { it !is Tail }) - 1 // empty head is always included
|
||||||
|
|
||||||
|
operator fun contains(element: E): Boolean = head.iterateBeforeNodeValue(element) !== tail
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun containsAll(elements: Collection<E>): Boolean = elements.all { contains(it) }
|
||||||
|
|
||||||
|
fun isEmpty(): Boolean = head.allMatching { it.isValidElementNode().not() }
|
||||||
|
|
||||||
|
fun forEach(block: (E) -> Unit) {
|
||||||
|
var node: Node<E> = head
|
||||||
|
while (true) {
|
||||||
|
if (node === tail) return
|
||||||
|
node.letIfNotnull(block)
|
||||||
|
node = node.nextNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAll(elements: Collection<E>) = elements.forEach { add(it) }
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun clear() {
|
||||||
|
val first = head.nextNode
|
||||||
|
head.nextNode = tail
|
||||||
|
first.childIterateReturnFirstUnsatisfying({
|
||||||
|
val n = it.nextNode
|
||||||
|
it.nextNode = tail
|
||||||
|
it.nodeValue = null
|
||||||
|
n
|
||||||
|
}, { it !== tail }) // clear the link structure, help GC.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
fun removeAll(elements: Collection<E>): Boolean = elements.all { remove(it) }
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
private fun removeNode(node: Node<E>): Boolean {
|
private fun removeNode(node: Node<E>): Boolean {
|
||||||
if (node == tail) {
|
if (node == tail) {
|
||||||
return false
|
return false
|
||||||
@ -78,7 +115,7 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
|
|||||||
if (toRemove == tail) { // This
|
if (toRemove == tail) { // This
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
toRemove.nodeValue = null // logaically remove first, then all the operations will recognize this node invalid
|
toRemove.nodeValue = null // logically remove first, then all the operations will recognize this node invalid
|
||||||
|
|
||||||
if (before.nextNodeRef.compareAndSet(toRemove, next)) { // physically remove: try to fix the link
|
if (before.nextNodeRef.compareAndSet(toRemove, next)) { // physically remove: try to fix the link
|
||||||
return true
|
return true
|
||||||
@ -86,43 +123,264 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val size: Int
|
fun removeAt(index: Int): E {
|
||||||
get() = head.countChildIterate<Node<E>>({ it.nextNodeRef.value }, { it !is Tail }) - 1 // empty head is always included
|
require(index >= 0) { "index must be >= 0" }
|
||||||
|
val nodeBeforeIndex = head.iterateValidNodeNTimes(index)
|
||||||
override operator fun contains(element: E): Boolean = head.iterateBeforeNodeValue(element) !== tail
|
val value = nodeBeforeIndex.nodeValue
|
||||||
|
if (value === null) noSuchElement()
|
||||||
override fun containsAll(elements: Collection<E>): Boolean = elements.all { contains(it) }
|
removeNode(nodeBeforeIndex)
|
||||||
|
return value
|
||||||
override operator fun get(index: Int): E {
|
|
||||||
require(index >= 0) { "Index must be >= 0" }
|
|
||||||
var i = index + 1 // 1 for head
|
|
||||||
return head.iterateStopOnFirst { i-- == 0 }.nodeValueRef.value ?: noSuchElement()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun indexOf(element: E): Int {
|
operator fun set(index: Int, element: E): E {
|
||||||
|
while (true) {
|
||||||
|
val nodeAtIndex = head.iterateValidNodeNTimes(index + 1)
|
||||||
|
val originalValue = nodeAtIndex.nodeValue
|
||||||
|
if (originalValue === null) noSuchElement() // this node has been removed.
|
||||||
|
if (!nodeAtIndex.nodeValueRef.compareAndSet(null, element)) { // with concurrent compatibility
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return originalValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the last index of the element in the list that is [equals] to [element], with concurrent compatibility.
|
||||||
|
*
|
||||||
|
* For a typical list, say `head <- Node#1(1) <- Node#2(2) <- Node#3(3) <- Node#4(4) <- Node#5(2) <- tail`,
|
||||||
|
* the procedures of `lastIndexOf(2)` is:
|
||||||
|
*
|
||||||
|
* 1. Iterate each element, until 2 is found, accumulate the index found, which is 1
|
||||||
|
* 2. Search again from the first matching element, which is Node#2
|
||||||
|
* 3. Accumulate the index found.
|
||||||
|
* 4. Repeat 2,3 until the `tail` is reached.
|
||||||
|
*
|
||||||
|
* Concurrent changes may influence the result.
|
||||||
|
*/
|
||||||
|
fun lastIndexOf(element: E): Int {
|
||||||
|
var lastMatching: Node<E> = head
|
||||||
|
var searchStartingFrom: Node<E> = lastMatching
|
||||||
|
var index = 0 // accumulated index from each search
|
||||||
|
|
||||||
|
findTheLastMatchingElement@ while (true) { // iterate to find the last matching element.
|
||||||
|
var timesOnThisTurn = if (searchStartingFrom === head) -1 else 0 // ignore the head
|
||||||
|
val got = searchStartingFrom.nextNode.iterateBeforeFirst { timesOnThisTurn++; it.nodeValue == element }
|
||||||
|
// find the first match starting from `searchStartingFrom`
|
||||||
|
|
||||||
|
if (got.isTail()) break@findTheLastMatchingElement // no further elements
|
||||||
|
check(timesOnThisTurn >= 0) { "Internal check failed: too many times ran: $timesOnThisTurn" }
|
||||||
|
|
||||||
|
searchStartingFrom = got.nextNode
|
||||||
|
index += timesOnThisTurn
|
||||||
|
|
||||||
|
if (!got.isRemoved()) lastMatching = got
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lastMatching.isValidElementNode()) {
|
||||||
|
// found is invalid means not found
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
override fun listIterator(): MutableListIterator<E> = listIterator0(0)
|
||||||
|
override fun listIterator(index: Int): MutableListIterator<E> = listIterator0(index)
|
||||||
|
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
private inline fun listIterator0(index: Int): MutableListIterator<E> {
|
||||||
|
var first: Node<E> = head
|
||||||
|
repeat(index) {
|
||||||
|
first = first.nextNode
|
||||||
|
if (first === tail) noSuchElement()
|
||||||
|
}
|
||||||
|
return object : MutableListIterator<E> {
|
||||||
|
var currentNode: Node<E>
|
||||||
|
get() = currentNodeRef.value
|
||||||
|
set(value) {
|
||||||
|
currentNodeRef.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private val currentNodeRef: AtomicRef<Node<E>> = atomic(first) // concurrent compatibility
|
||||||
|
|
||||||
|
override fun nextIndex(): Int = indexOfNode(currentNode)
|
||||||
|
|
||||||
|
|
||||||
|
// region previous
|
||||||
|
|
||||||
|
var previousNode: Node<E>
|
||||||
|
get() = previousNodeRef.value
|
||||||
|
set(value) {
|
||||||
|
previousNodeRef.value = value
|
||||||
|
}
|
||||||
|
private val previousNodeRef: AtomicRef<Node<E>> = atomic(head) // concurrent compatibility
|
||||||
|
private val previousNodeIndexRef: AtomicInt = atomic(-1) // concurrent compatibility
|
||||||
|
private val currentNodeAtTheMomentWhenPreviousNodeIsUpdated: AtomicRef<Node<E>> = atomic(currentNode)
|
||||||
|
|
||||||
|
override fun hasPrevious(): Boolean = previousIndex() == -1
|
||||||
|
|
||||||
|
private fun updatePrevious(): Boolean {
|
||||||
|
while (true) {
|
||||||
|
val localNodeAtTheMomentWhenPreviousNodeIsUpdated = currentNode
|
||||||
|
var i = -1 // head
|
||||||
|
var lastSatisfying: Node<E>? = null
|
||||||
|
val foundNode = currentNode.childIterateReturnsLastSatisfying({ it.nextNode }, {
|
||||||
|
i++
|
||||||
|
if (it.isValidElementNode()) {
|
||||||
|
lastSatisfying = it
|
||||||
|
}
|
||||||
|
it != currentNode
|
||||||
|
})
|
||||||
|
|
||||||
|
if (localNodeAtTheMomentWhenPreviousNodeIsUpdated !== currentNode) {
|
||||||
|
continue // current is concurrently changed, must retry
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundNode.isValidElementNode()) {
|
||||||
|
// Current node is not found in the list, meaning it had been removed concurrently
|
||||||
|
previousNode = head
|
||||||
|
previousNodeIndexRef.value = -1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSatisfying === null) {
|
||||||
|
// All the previous nodes are logically removed.
|
||||||
|
previousNode = head
|
||||||
|
previousNodeIndexRef.value = -1
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value = localNodeAtTheMomentWhenPreviousNodeIsUpdated
|
||||||
|
previousNode = lastSatisfying!! // false positive nullable warning
|
||||||
|
previousNodeIndexRef.value = i
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun previous(): E {
|
||||||
|
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
|
||||||
|
// node list have been changed.
|
||||||
|
if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
val value = previousNode.nodeValue
|
||||||
|
if (value != null) {
|
||||||
|
currentNode = previousNode
|
||||||
|
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head
|
||||||
|
return value
|
||||||
|
} else if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun previousIndex(): Int {
|
||||||
|
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
|
||||||
|
// node list have been changed.
|
||||||
|
if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
val value = previousNodeIndexRef.value
|
||||||
|
if (value != -1) return value
|
||||||
|
else if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
override fun add(element: E) {
|
||||||
|
val toAdd = element.asNode(tail)
|
||||||
|
while (true) {
|
||||||
|
val next = currentNode.nextNode
|
||||||
|
toAdd.nextNode = next
|
||||||
|
if (currentNode.nextNodeRef.compareAndSet(next, toAdd)) { // ensure the link is not changed
|
||||||
|
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value = head
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hasNext(): Boolean {
|
||||||
|
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
|
||||||
|
// node list have been changed.
|
||||||
|
if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
return currentNode.nextNode !== tail
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun next(): E {
|
||||||
|
while (true) {
|
||||||
|
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
|
||||||
|
// node list have been changed.
|
||||||
|
if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
val nextNodeValue = currentNode.nextNode.nodeValue
|
||||||
|
if (nextNodeValue !== null) {
|
||||||
|
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value = head
|
||||||
|
return nextNodeValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun remove() {
|
||||||
|
if (!removeNode(currentNode)) { // search from head onto the node, concurrent compatibility
|
||||||
|
noSuchElement()
|
||||||
|
}
|
||||||
|
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value = head
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(element: E) {
|
||||||
|
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
|
||||||
|
// node list have been changed.
|
||||||
|
if (!updatePrevious()) noSuchElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E> {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
operator fun get(index: Int): E {
|
||||||
|
require(index >= 0) { "Index must be >= 0" }
|
||||||
|
var i = index + 1 // 1 for head
|
||||||
|
return head.iterateStopOnFirst { i-- == 0 }.nodeValue ?: noSuchElement()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun indexOf(element: E): Int {
|
||||||
var i = -1 // head
|
var i = -1 // head
|
||||||
if (!head.iterateStopOnFirst {
|
if (!head.iterateStopOnFirst {
|
||||||
i++
|
i++
|
||||||
it.nodeValueRef.value == element
|
it.nodeValue == element
|
||||||
}.isValidElementNode()) {
|
}.isValidElementNode()) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return i - 1 // iteration is stopped at the next node
|
return i - 1 // iteration is stopped at the next node
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isEmpty(): Boolean = head.allMatching { it.nodeValueRef.value == null }
|
private fun indexOfNode(node: Node<E>): Int {
|
||||||
|
var i = -1 // head
|
||||||
|
if (!head.iterateStopOnFirst {
|
||||||
|
i++
|
||||||
|
it == node
|
||||||
|
}.isValidElementNode()) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return i - 1 // iteration is stopped at the next node
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
operator fun iterator(): MutableIterator<E> = object : MutableIterator<E> {
|
||||||
* Create a concurrent-unsafe iterator
|
|
||||||
*/
|
|
||||||
override operator fun iterator(): MutableIterator<E> = object : MutableIterator<E> {
|
|
||||||
var currentNode: Node<E>
|
var currentNode: Node<E>
|
||||||
get() = currentNoderef.value
|
get() = currentNodeRef.value
|
||||||
set(value) {
|
set(value) {
|
||||||
currentNoderef.value = value
|
currentNodeRef.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentNoderef: AtomicRef<Node<E>> = atomic(head) // concurrent compatibility
|
private val currentNodeRef: AtomicRef<Node<E>> = atomic(head) // concurrent compatibility
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if
|
* Check if
|
||||||
@ -156,103 +414,26 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
/**
|
|
||||||
* Find the last index of the element in the list that is [equals] to [element], with concurrent compatibility.
|
|
||||||
*
|
|
||||||
* For a typical list, say `head <- Node#1(1) <- Node#2(2) <- Node#3(3) <- Node#4(4) <- Node#5(2) <- tail`,
|
|
||||||
* the procedures of `lastIndexOf(2)` is:
|
|
||||||
*
|
|
||||||
* 1. Iterate each element, until 2 is found, accumulate the index found, which is 1
|
|
||||||
* 2. Search again from the first matching element, which is Node#2
|
|
||||||
* 3. Accumulate the index found.
|
|
||||||
* 4. Repeat 2,3 until the `tail` is reached.
|
|
||||||
*
|
|
||||||
* Concurrent changes may influence the result.
|
|
||||||
* While searching,
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
override fun lastIndexOf(element: E): Int {
|
|
||||||
var lastMatching: Node<E> = head
|
|
||||||
var searchStartingFrom: Node<E> = lastMatching
|
|
||||||
var index = 0 // accumulated index from each search
|
|
||||||
|
|
||||||
findTheLastMatchingElement@ while (true) { // iterate to find the last matching element.
|
|
||||||
var timesOnThisTurn = if (searchStartingFrom === head) -1 else 0 // ignore the head
|
|
||||||
val got = searchStartingFrom.nextNode.iterateBeforeFirst { timesOnThisTurn++; it.nodeValue == element }
|
|
||||||
// find the first match starting from `searchStartingFrom`
|
|
||||||
|
|
||||||
if (got.isTail()) break@findTheLastMatchingElement // no further elements
|
|
||||||
check(timesOnThisTurn >= 0) { "Internal check failed: too many times ran: $timesOnThisTurn" }
|
|
||||||
|
|
||||||
searchStartingFrom = got.nextNode
|
|
||||||
index += timesOnThisTurn
|
|
||||||
|
|
||||||
if (!got.isRemoved()) lastMatching = got //record the lastMatching only if got is not removed.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lastMatching.isValidElementNode()) {
|
|
||||||
// found is invalid means not found
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return index
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun listIterator(): MutableListIterator<E> = listIterator0(0)
|
|
||||||
override fun listIterator(index: Int): MutableListIterator<E> = listIterator0(index)
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
internal inline fun listIterator0(index: Int): MutableListIterator<E> {
|
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E> {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun add(index: Int, element: E) {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addAll(index: Int, elements: Collection<E>): Boolean {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addAll(elements: Collection<E>): Boolean {
|
|
||||||
elements.forEach { add(it) }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun clear() {
|
|
||||||
head.nextNode = tail
|
|
||||||
|
|
||||||
// TODO: 2019/12/13 check ?
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAll(elements: Collection<E>): Boolean {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeAt(index: Int): E {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun retainAll(elements: Collection<E>): Boolean {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
override operator fun set(index: Int, element: E): E {
|
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NO INLINE: currently exceptions thrown in a inline function cannot be traced
|
|
||||||
private fun noSuchElement(): Nothing = throw NoSuchElementException()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [List] containing all the elements in [this] in the same order
|
||||||
|
*/
|
||||||
|
fun <E> LockFreeLinkedList<E>.toList(): List<E> = toMutableList()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a [MutableList] containing all the elements in [this] in the same order
|
||||||
|
*/
|
||||||
|
fun <E> LockFreeLinkedList<E>.toMutableList(): MutableList<E> {
|
||||||
|
val list = mutableListOf<E>()
|
||||||
|
this.forEach { list.add(it) }
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// region internal
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun <E> E.asNode(nextNode: Node<E>): Node<E> = Node(nextNode).apply { nodeValueRef.value = this@asNode }
|
private inline fun <E> E.asNode(nextNode: Node<E>): Node<E> = Node(nextNode).apply { nodeValueRef.value = this@asNode }
|
||||||
|
|
||||||
@ -280,7 +461,7 @@ private inline fun <N : Node<*>> N.childIterateReturnsLastSatisfying(iterator: (
|
|||||||
* Self-iterate using the [iterator], until [mustBeTrue] returns `false`.
|
* Self-iterate using the [iterator], until [mustBeTrue] returns `false`.
|
||||||
* Returns the element at the first time when the [mustBeTrue] returns `false`
|
* Returns the element at the first time when the [mustBeTrue] returns `false`
|
||||||
*/
|
*/
|
||||||
private inline fun <E> E.childIterateReturnFirstUnsitisfying(iterator: (E) -> E, mustBeTrue: (E) -> Boolean): E {
|
private inline fun <E> E.childIterateReturnFirstUnsatisfying(iterator: (E) -> E, mustBeTrue: (E) -> Boolean): E {
|
||||||
if (!mustBeTrue(this)) return this
|
if (!mustBeTrue(this)) return this
|
||||||
var value: E = this
|
var value: E = this
|
||||||
|
|
||||||
@ -318,14 +499,12 @@ private inline fun <E> E.countChildIterate(iterator: (E) -> E, mustBeTrue: (E) -
|
|||||||
|
|
||||||
private class Head<E>(
|
private class Head<E>(
|
||||||
nextNode: Node<E>
|
nextNode: Node<E>
|
||||||
) : Node<E>(nextNode) {
|
) : Node<E>(nextNode)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private open class Node<E>(
|
private open class Node<E>(
|
||||||
nextNode: Node<E>?
|
nextNode: Node<E>?
|
||||||
) {
|
) {
|
||||||
internal val id: Int = nextId();
|
internal val id: Int = nextId()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val idCount = atomic(0)
|
private val idCount = atomic(0)
|
||||||
@ -349,6 +528,11 @@ private open class Node<E>(
|
|||||||
@Suppress("LeakingThis")
|
@Suppress("LeakingThis")
|
||||||
val nextNodeRef: AtomicRef<Node<E>> = atomic(nextNode ?: this)
|
val nextNodeRef: AtomicRef<Node<E>> = atomic(nextNode ?: this)
|
||||||
|
|
||||||
|
inline fun <R> letIfNotnull(block: (E) -> R): R? {
|
||||||
|
val value = this.nodeValue
|
||||||
|
return if (value !== null) block(value) else null
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Short cut for accessing [nextNodeRef]
|
* Short cut for accessing [nextNodeRef]
|
||||||
*/
|
*/
|
||||||
@ -360,38 +544,10 @@ private open class Node<E>(
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the former node of the last node whence [filter] returnes true
|
* Returns the former node of the last node whence [filter] returns true
|
||||||
*/
|
*/
|
||||||
inline fun iterateBeforeFirst(filter: (Node<E>) -> Boolean): Node<E> =
|
inline fun iterateBeforeFirst(filter: (Node<E>) -> Boolean): Node<E> =
|
||||||
this.childIterateReturnsLastSatisfying<Node<E>>({ it.nextNode }, { !filter(it) })
|
this.childIterateReturnsLastSatisfying({ it.nextNode }, { !filter(it) })
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the last node whence [filter] returnes true.
|
|
||||||
*/
|
|
||||||
inline fun iterateStopOnFirst(filter: (Node<E>) -> Boolean): Node<E> =
|
|
||||||
iterateBeforeFirst(filter).nextNode
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the former node of next node whose value is not null.
|
|
||||||
* [Tail] is returned if no node is found
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun iterateBeforeNotnull(): Node<E> = iterateBeforeFirst { it.nodeValue != null }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next node which is not [Tail] or [Head] and with a notnull value.
|
|
||||||
* [Tail] is returned if no node is found
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun nextValidElement(): Node<E> = this.iterateBeforeFirst { !it.isValidElementNode() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the next node whose value is not null.
|
|
||||||
* [Tail] is returned if no node is found
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun nextNotnull(): Node<E> = this.iterateBeforeFirst { it.nodeValueRef.value == null }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if all the node which is not [Tail] matches the [condition]
|
* Check if all the node which is not [Tail] matches the [condition]
|
||||||
@ -399,7 +555,7 @@ private open class Node<E>(
|
|||||||
* Head, which is this, is also being tested.
|
* Head, which is this, is also being tested.
|
||||||
* [Tail], is not being tested.
|
* [Tail], is not being tested.
|
||||||
*/
|
*/
|
||||||
inline fun allMatching(condition: (Node<E>) -> Boolean): Boolean = this.childIterateReturnsLastSatisfying<Node<E>>({ it.nextNode }, condition) !is Tail
|
inline fun allMatching(condition: (Node<E>) -> Boolean): Boolean = this.childIterateReturnsLastSatisfying({ it.nextNode }, condition) !is Tail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop on and returns the former element of the element that is [equals] to the [element]
|
* Stop on and returns the former element of the element that is [equals] to the [element]
|
||||||
@ -408,21 +564,10 @@ private open class Node<E>(
|
|||||||
*/
|
*/
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
inline fun iterateBeforeNodeValue(element: E): Node<E> = this.iterateBeforeFirst { it.nodeValueRef.value == element }
|
inline fun iterateBeforeNodeValue(element: E): Node<E> = this.iterateBeforeFirst { it.nodeValueRef.value == element }
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop on and returns the element that is [equals] to the [element]
|
|
||||||
*
|
|
||||||
* E.g.: for `head <- 1 <- 2 <- 3 <- tail`, `iterateStopOnNodeValue(2)` returns the node whose value is 2
|
|
||||||
*/
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
inline fun iterateStopOnNodeValue(element: E): Node<E> = this.iterateBeforeNodeValue(element).nextNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private open class Tail<E> : Node<E>(null)
|
private open class Tail<E> : Node<E>(null)
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
private fun <E> AtomicRef<out Node<out E>>.getNodeValue(): E? = if (this.value is Tail) null else this.value.nodeValueRef.value
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun Node<*>.isValidElementNode(): Boolean = !isHead() && !isTail() && !isRemoved()
|
private inline fun Node<*>.isValidElementNode(): Boolean = !isHead() && !isTail() && !isRemoved()
|
||||||
|
|
||||||
@ -434,3 +579,5 @@ private inline fun Node<*>.isTail(): Boolean = this is Tail
|
|||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
private inline fun Node<*>.isRemoved(): Boolean = this.nodeValue == null
|
private inline fun Node<*>.isRemoved(): Boolean = this.nodeValue == null
|
||||||
|
|
||||||
|
// en dregion
|
@ -4,17 +4,18 @@ package net.mamoe.mirai.utils
|
|||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import net.mamoe.mirai.test.shouldBeEqualTo
|
import net.mamoe.mirai.test.shouldBeEqualTo
|
||||||
import net.mamoe.mirai.test.shouldBeFalse
|
|
||||||
import net.mamoe.mirai.test.shouldBeTrue
|
import net.mamoe.mirai.test.shouldBeTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
import kotlin.test.*
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
@MiraiExperimentalAPI
|
||||||
internal class LockFreeLinkedListTest {
|
internal class LockFreeLinkedListTest {
|
||||||
init {
|
init {
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
delay(5000)
|
delay(30 * 1000)
|
||||||
exitProcess(-100)
|
exitProcess(-100)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,26 +28,41 @@ internal class LockFreeLinkedListTest {
|
|||||||
list.add(3)
|
list.add(3)
|
||||||
list.add(4)
|
list.add(4)
|
||||||
|
|
||||||
assertEquals(list[0], 1, "Failed on list[0]")
|
list.size shouldBeEqualTo 4
|
||||||
assertEquals(list[1], 2, "Failed on list[1]")
|
|
||||||
assertEquals(list[2], 3, "Failed on list[2]")
|
|
||||||
assertEquals(list[3], 4, "Failed on list[3]")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addAndGetSingleConcurrent() {
|
fun addAndGetConcurrent() = runBlocking {
|
||||||
|
//withContext(Dispatchers.Default){
|
||||||
val list = LockFreeLinkedList<Int>()
|
val list = LockFreeLinkedList<Int>()
|
||||||
val add = GlobalScope.async { list.concurrentAdd(1000, 10, 1) }
|
|
||||||
val remove = GlobalScope.async {
|
list.concurrentAdd(1000, 10, 1)
|
||||||
add.join()
|
list.size shouldBeEqualTo 1000 * 10
|
||||||
list.concurrentDo(100, 10) {
|
|
||||||
remove(1)
|
list.concurrentDo(100, 10) {
|
||||||
}
|
remove(1).shouldBeTrue()
|
||||||
}
|
}
|
||||||
runBlocking {
|
|
||||||
joinAll(add, remove)
|
list.size shouldBeEqualTo 1000 * 10 - 100 * 10
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addAndGetMassConcurrentAccess() = runBlocking {
|
||||||
|
val list = LockFreeLinkedList<Int>()
|
||||||
|
|
||||||
|
val addJob = async { list.concurrentAdd(5000, 10, 1) }
|
||||||
|
|
||||||
|
delay(10) // let addJob fly
|
||||||
|
if (addJob.isCompleted) {
|
||||||
|
error("Number of elements are not enough")
|
||||||
}
|
}
|
||||||
assertEquals(1000 * 10 - 100 * 10, list.size)
|
list.concurrentDo(1000, 10) {
|
||||||
|
remove(1).shouldBeTrue()
|
||||||
|
}
|
||||||
|
addJob.join()
|
||||||
|
|
||||||
|
list.size shouldBeEqualTo 5000 * 10 - 1000 * 10
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -65,179 +81,113 @@ internal class LockFreeLinkedListTest {
|
|||||||
assertEquals(1, list.size)
|
assertEquals(1, list.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun getSize() {
|
|
||||||
val list = lockFreeLinkedListOf(1, 2, 3, 4, 5)
|
|
||||||
assertEquals(5, list.size)
|
|
||||||
|
|
||||||
val list2 = lockFreeLinkedListOf<Unit>()
|
|
||||||
assertEquals(0, list2.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun contains() {
|
|
||||||
val list = lockFreeLinkedListOf<Int>()
|
|
||||||
assertFalse { list.contains(0) }
|
|
||||||
|
|
||||||
list.add(0)
|
|
||||||
assertTrue { list.contains(0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun containsAll() {
|
|
||||||
var list = lockFreeLinkedListOf(1, 2, 3)
|
|
||||||
assertTrue { list.containsAll(listOf(1, 2, 3)) }
|
|
||||||
assertTrue { list.containsAll(listOf()) }
|
|
||||||
|
|
||||||
list = lockFreeLinkedListOf(1, 2)
|
|
||||||
assertFalse { list.containsAll(listOf(1, 2, 3)) }
|
|
||||||
|
|
||||||
list = lockFreeLinkedListOf()
|
|
||||||
assertTrue { list.containsAll(listOf()) }
|
|
||||||
assertFalse { list.containsAll(listOf(1)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun indexOf() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 3, 3)
|
|
||||||
assertEquals(0, list.indexOf(1))
|
|
||||||
assertEquals(2, list.indexOf(3))
|
|
||||||
|
|
||||||
assertEquals(-1, list.indexOf(4))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun isEmpty() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf()
|
|
||||||
list.isEmpty().shouldBeTrue()
|
|
||||||
|
|
||||||
list.add(1)
|
|
||||||
list.isEmpty().shouldBeFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun iterator() {
|
|
||||||
var list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
|
|
||||||
list.forEach {
|
|
||||||
it shouldBeEqualTo 2
|
|
||||||
}
|
|
||||||
|
|
||||||
list = lockFreeLinkedListOf(1, 2)
|
|
||||||
list.joinToString { it.toString() } shouldBeEqualTo "1, 2"
|
|
||||||
|
|
||||||
|
|
||||||
list = lockFreeLinkedListOf(1, 2)
|
|
||||||
val iterator = list.iterator()
|
|
||||||
iterator.remove()
|
|
||||||
var reached = false
|
|
||||||
for (i in iterator) {
|
|
||||||
i shouldBeEqualTo 2
|
|
||||||
reached = true
|
|
||||||
}
|
|
||||||
reached shouldBeEqualTo true
|
|
||||||
|
|
||||||
list.joinToString { it.toString() } shouldBeEqualTo "2"
|
|
||||||
iterator.remove()
|
|
||||||
assertFailsWith<NoSuchElementException> { iterator.remove() }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `lastIndexOf of exact 1 match at first`() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2, 1)
|
|
||||||
list.lastIndexOf(2) shouldBeEqualTo 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `lastIndexOf of exact 1 match`() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2)
|
|
||||||
list.lastIndexOf(2) shouldBeEqualTo 1
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `lastIndexOf of multiply matches`() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 2)
|
|
||||||
list.lastIndexOf(2) shouldBeEqualTo 2
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `lastIndexOf of no match`() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
|
|
||||||
list.lastIndexOf(3) shouldBeEqualTo -1
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `lastIndexOf of many elements`() {
|
|
||||||
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 4, 2, 3, 4, 5)
|
|
||||||
list.lastIndexOf(4) shouldBeEqualTo 4
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
companion object{
|
|
||||||
@JvmStatic
|
|
||||||
fun main(vararg args: String) {
|
|
||||||
LockFreeLinkedListTest().`lastIndexOf of many elements`()
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun listIterator() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testListIterator() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun subList() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testAdd() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addAll() {
|
fun addAll() {
|
||||||
}
|
val list = LockFreeLinkedList<Int>()
|
||||||
|
list.addAll(listOf(1, 2, 3, 4, 5))
|
||||||
@Test
|
list.size shouldBeEqualTo 5
|
||||||
fun testAddAll() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun clear() {
|
fun clear() {
|
||||||
|
val list = LockFreeLinkedList<Int>()
|
||||||
|
list.addAll(listOf(1, 2, 3, 4, 5))
|
||||||
|
list.size shouldBeEqualTo 5
|
||||||
|
list.clear()
|
||||||
|
list.size shouldBeEqualTo 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseExperimental(ExperimentalUnsignedTypes::class)
|
||||||
|
@Test
|
||||||
|
fun withInlineClassElements() {
|
||||||
|
val list = LockFreeLinkedList<UInt>()
|
||||||
|
list.addAll(listOf(1u, 2u, 3u, 4u, 5u))
|
||||||
|
list.size shouldBeEqualTo 5
|
||||||
|
|
||||||
|
list.toString() shouldBeEqualTo "[1, 2, 3, 4, 5]"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@Test
|
||||||
|
fun indexOf() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 3, 3)
|
||||||
|
assertEquals(0, list.indexOf(1))
|
||||||
|
assertEquals(2, list.indexOf(3))
|
||||||
|
|
||||||
|
assertEquals(-1, list.indexOf(4))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeAll() {
|
fun iterator() {
|
||||||
|
var list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
|
||||||
|
list.forEach {
|
||||||
|
it shouldBeEqualTo 2
|
||||||
|
}
|
||||||
|
|
||||||
|
list = lockFreeLinkedListOf(1, 2)
|
||||||
|
list.joinToString { it.toString() } shouldBeEqualTo "1, 2"
|
||||||
|
|
||||||
|
|
||||||
|
list = lockFreeLinkedListOf(1, 2)
|
||||||
|
val iterator = list.iterator()
|
||||||
|
iterator.remove()
|
||||||
|
var reached = false
|
||||||
|
for (i in iterator) {
|
||||||
|
i shouldBeEqualTo 2
|
||||||
|
reached = true
|
||||||
|
}
|
||||||
|
reached shouldBeEqualTo true
|
||||||
|
|
||||||
|
list.joinToString { it.toString() } shouldBeEqualTo "2"
|
||||||
|
iterator.remove()
|
||||||
|
assertFailsWith<NoSuchElementException> { iterator.remove() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeAt() {
|
fun `lastIndexOf of exact 1 match at first`() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2, 1)
|
||||||
|
list.lastIndexOf(2) shouldBeEqualTo 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun retainAll() {
|
fun `lastIndexOf of exact 1 match`() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2)
|
||||||
|
list.lastIndexOf(2) shouldBeEqualTo 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun set() {
|
fun `lastIndexOf of multiply matches`() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 2)
|
||||||
|
list.lastIndexOf(2) shouldBeEqualTo 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `lastIndexOf of no match`() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
|
||||||
|
list.lastIndexOf(3) shouldBeEqualTo -1
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `lastIndexOf of many elements`() {
|
||||||
|
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 4, 2, 3, 4, 5)
|
||||||
|
list.lastIndexOf(4) shouldBeEqualTo 4
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun withTimeoutBlocking(timeout: Long = 500L, block: suspend () -> Unit) = runBlocking { withTimeout(timeout) { block() } }
|
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
@MiraiExperimentalAPI
|
||||||
internal suspend fun <E> LockFreeLinkedList<E>.concurrentAdd(numberOfCoroutines: Int, timesOfAdd: Int, element: E) =
|
internal suspend inline fun <E> LockFreeLinkedList<E>.concurrentAdd(numberOfCoroutines: Int, timesOfAdd: Int, element: E) =
|
||||||
concurrentDo(numberOfCoroutines, timesOfAdd) { add(element) }
|
concurrentDo(numberOfCoroutines, timesOfAdd) { add(element) }
|
||||||
|
|
||||||
@MiraiExperimentalAPI
|
@MiraiExperimentalAPI
|
||||||
internal suspend fun <E : LockFreeLinkedList<*>> E.concurrentDo(numberOfCoroutines: Int, timesOfAdd: Int, todo: E.() -> Unit) = coroutineScope {
|
internal suspend inline fun <E : LockFreeLinkedList<*>> E.concurrentDo(numberOfCoroutines: Int, timesOfAdd: Int, crossinline todo: E.() -> Unit) =
|
||||||
repeat(numberOfCoroutines) {
|
coroutineScope {
|
||||||
launch {
|
repeat(numberOfCoroutines) {
|
||||||
repeat(timesOfAdd) {
|
launch {
|
||||||
todo()
|
repeat(timesOfAdd) {
|
||||||
|
todo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user