[Review] Remove LockFreeLinkedList

This commit is contained in:
Him188 2021-01-06 15:16:47 +08:00
parent 24fdf056c2
commit 1783a69be2
4 changed files with 10 additions and 1174 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -11,7 +11,6 @@
package net.mamoe.mirai.contact
import net.mamoe.mirai.utils.LockFreeLinkedList
import net.mamoe.mirai.utils.MiraiInternalApi
import java.util.concurrent.ConcurrentLinkedQueue
@ -46,15 +45,4 @@ internal constructor(@JvmField @MiraiInternalApi public val delegate: Concurrent
* ```
*/
public val ContactList<*>.idContentString: String
get() = "[" + delegate.joinToString { it.id.toString() } + "]"
internal operator fun <C : Contact> LockFreeLinkedList<C>.get(id: Long): C {
forEach { if (it.id == id) return it }
throw NoSuchElementException("No such contact: $id")
}
internal fun <C : Contact> LockFreeLinkedList<C>.getOrNull(id: Long): C? {
forEach { if (it.id == id) return it }
return null
}
get() = "[" + delegate.joinToString { it.id.toString() } + "]"

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -18,7 +18,9 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.message.MessageSerializer
import net.mamoe.mirai.utils.*
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.utils.MiraiInternalApi
import java.util.concurrent.ConcurrentLinkedQueue
/**
* 自定义消息
@ -114,15 +116,15 @@ public sealed class CustomMessage : SingleMessage {
}
public companion object {
private val factories: LockFreeLinkedList<Factory<*>> = LockFreeLinkedList()
private val factories: ConcurrentLinkedQueue<Factory<*>> = ConcurrentLinkedQueue()
internal fun register(factory: Factory<out CustomMessage>) {
factories.removeIf { it::class == factory::class }
val exist = factories.asSequence().firstOrNull { it.typeName == factory.typeName }
val exist = factories.firstOrNull { it.typeName == factory.typeName }
if (exist != null) {
error("CustomMessage.Factory typeName ${factory.typeName} is already registered by ${exist::class.qualifiedName}")
}
factories.addLast(factory)
factories.add(factory)
}
@Serializable
@ -149,7 +151,7 @@ public sealed class CustomMessage : SingleMessage {
}
return kotlin.runCatching {
when (msg.miraiVersionFlag) {
1 -> factories.asSequence().firstOrNull { it.typeName == msg.typeName }?.load(msg.data)
1 -> factories.firstOrNull { it.typeName == msg.typeName }?.load(msg.data)
else -> null
}
}.getOrElse {

View File

@ -1,865 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("NOTHING_TO_INLINE", "unused")
package net.mamoe.mirai.utils
import kotlinx.atomicfu.AtomicBoolean
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.loop
/**
* Collect all the elements into a [MutableList] then cast it as a [List]
*/
internal fun <E> LockFreeLinkedList<E>.toList(): List<E> = toMutableList()
/**
* Collect all the elements into a [MutableList].
*/
internal fun <E> LockFreeLinkedList<E>.toMutableList(): MutableList<E> {
val list = mutableListOf<E>()
this.forEach { list.add(it) }
return list
}
/**
* Collect all the elements into a [MutableSet] then cast it as a [Set]
*/
internal fun <E> LockFreeLinkedList<E>.toSet(): Set<E> = toMutableSet()
/**
* Collect all the elements into a [MutableSet].
*/
internal fun <E> LockFreeLinkedList<E>.toMutableSet(): MutableSet<E> {
val list = mutableSetOf<E>()
this.forEach { list.add(it) }
return list
}
/**
* Builds a [Sequence] containing all the elements in [this] in the same order.
*
* Note that the sequence is dynamic
*/
internal fun <E> LockFreeLinkedList<E>.asSequence(): Sequence<E> {
return generateSequence(head) { current: LockFreeLinkedListNode<E> ->
current.nextValidNode(until = tail).takeIf { it != tail }
}.drop(1) // drop head, should be dropped lazily
.map { it.nodeValue }
}
internal fun <E> LockFreeLinkedListNode<E>.nextValidNode(until: LockFreeLinkedListNode<E>): LockFreeLinkedListNode<E> {
var node: LockFreeLinkedListNode<E> = this.nextNode
while (node != until) {
if (node.isValidElementNode()) {
return node
}
node = node.nextNode
}
return node
}
internal operator fun <E> LockFreeLinkedList<E>.iterator(): Iterator<E> {
return asSequence().iterator()
}
/**
* 构建链表结构然后转为 [LockFreeLinkedList]
*/
internal fun <E> Iterable<E>.toLockFreeLinkedList(): LockFreeLinkedList<E> {
return LockFreeLinkedList<E>().apply { addAll(this@toLockFreeLinkedList) }
}
/**
* 构建链表结构然后转为 [LockFreeLinkedList]
*/
internal fun <E> Sequence<E>.toLockFreeLinkedList(): LockFreeLinkedList<E> {
return LockFreeLinkedList<E>().apply { addAll(this@toLockFreeLinkedList) }
}
/**
* Implementation of lock-free LinkedList.
*
* Modifying can be performed concurrently.
* Iterating concurrency is guaranteed.
*/
internal open class LockFreeLinkedList<E> {
@PublishedApi
internal val tail: Tail<E> = Tail()
@PublishedApi
internal val head: Head<E> = Head(tail)
open fun removeFirst(): E {
while (true) {
val currentFirst = head.nextNode
if (!currentFirst.isValidElementNode()) {
throw NoSuchElementException()
}
if (head.compareAndSetNextNodeRef(currentFirst, currentFirst.nextNode)) {
return currentFirst.nodeValue
}
}
}
open fun peekFirst(): E? {
return head
.iterateBeforeFirst { it.isValidElementNode() }
.takeUnless { it.isTail() }
?.nextNode
?.nodeValue
}
open fun removeLast(): E {
while (true) {
val beforeLast = head.iterateBeforeFirst { it.nextNode === tail }
if (!beforeLast.isValidElementNode()) {
throw NoSuchElementException()
}
val last = beforeLast.nextNode
if (beforeLast.nextNodeRef.compareAndSet(last, last.nextNode)) {
return last.nodeValue
}
}
}
open fun addLast(element: E) {
addLastNode(element.asNode(tail))
}
private fun addLastNode(node: LockFreeLinkedListNode<E>) {
while (true) {
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
return
}
}
}
open fun tryInsertAfter(node: LockFreeLinkedListNode<E>, newValue: E): Boolean {
if (node == tail) {
error("Cannot insert value after tail")
}
if (node.isRemoved()) {
return false
}
val next = node.nextNodeRef.value
val newNode = newValue.asNode(next)
return node.nextNodeRef.compareAndSet(next, newNode)
}
/**
* 先把元素建立好链表, 再加入到 list.
*/
@Suppress("DuplicatedCode")
open fun addAll(iterable: Iterable<E>) {
var firstNode: LockFreeLinkedListNode<E>? = null
var currentNode: LockFreeLinkedListNode<E>? = null
iterable.forEach {
val nextNode = it.asNode(tail)
if (firstNode == null) {
firstNode = nextNode
}
currentNode?.nextNode = nextNode
currentNode = nextNode
}
firstNode?.let { addLastNode(it) }
}
/**
* 先把元素建立好链表, 再加入到 list.
*/
@Suppress("DuplicatedCode")
open fun addAll(iterable: Sequence<E>) {
var firstNode: LockFreeLinkedListNode<E>? = null
var currentNode: LockFreeLinkedListNode<E>? = null
iterable.forEach {
val nextNode = it.asNode(tail)
if (firstNode == null) {
firstNode = nextNode
}
currentNode?.nextNode = nextNode
currentNode = nextNode
}
firstNode?.let { addLastNode(it) }
}
open operator fun plusAssign(element: E) = this.addLast(element)
/**
* 过滤并获取, 获取不到则添加一个元素.
*/
fun filteringGetOrAdd(filter: (E) -> Boolean, supplier: () -> E): E {
val node = LazyNode(tail, supplier)
while (true) {
var current: LockFreeLinkedListNode<E> = head
findLastNode@ while (true) {
if (current.isValidElementNode() && filter(current.nodeValue))
return current.nodeValue
if (current.nextNode === tail) {
if (current.compareAndSetNextNodeRef(
tail,
node
)
) { // ensure only one attempt can put the lazyNode in
return node.nodeValue
}
}
current = current.nextNode
}
}
}
@PublishedApi // limitation by atomicfu
internal fun <E> LockFreeLinkedListNode<E>.compareAndSetNextNodeRef(
expect: LockFreeLinkedListNode<E>,
update: LockFreeLinkedListNode<E>
): Boolean =
this.nextNodeRef.compareAndSet(expect, update)
override fun toString(): String = "[" + asSequence().joinToString() + "]"
@Suppress("unused")
internal fun getLinkStructure(): String = buildString {
head.childIterateReturnsLastSatisfying<LockFreeLinkedListNode<*>>({
append(it.toString())
append(" <- ")
it.nextNode
}, { it !is Tail })
}.dropLast(4)
@Suppress("DuplicatedCode")
fun removeIf(filter: (E) -> Boolean) {
while (true) {
val before = head.iterateBeforeFirst { it.isValidElementNode() && filter(it.nodeValue) }
val toRemove = before.nextNode
if (toRemove === tail) {
return
}
if (toRemove.isRemoved()) {
continue
}
@Suppress("BooleanLiteralArgument") // false positive
if (!toRemove.removed.compareAndSet(false, true)) {
// logically remove: all the operations will recognize this node invalid
continue
}
// physically remove: try to fix the link
var next: LockFreeLinkedListNode<E> = toRemove.nextNode
while (next !== tail && next.isRemoved()) {
next = next.nextNode
}
if (before.nextNodeRef.compareAndSet(toRemove, next)) {
return
}
}
}
@Suppress("DuplicatedCode")
open fun remove(element: E): Boolean {
while (true) {
val before = head.iterateBeforeNodeValue(element)
val toRemove = before.nextNode
if (toRemove === tail) {
return false
}
if (toRemove.isRemoved()) {
continue
}
@Suppress("BooleanLiteralArgument") // false positive
if (!toRemove.removed.compareAndSet(false, true)) {
// logically remove: all the operations will recognize this node invalid
continue
}
// physically remove: try to fix the link
var next: LockFreeLinkedListNode<E> = toRemove.nextNode
while (next !== tail && next.isRemoved()) {
next = next.nextNode
}
if (before.nextNodeRef.compareAndSet(toRemove, next)) {
return true
}
}
}
/**
* 动态计算的大小
*/
val size: Int
get() = head.countChildIterate<LockFreeLinkedListNode<E>>(
{ it.nextNode },
{ it !is Tail }) - 1 // empty head is always included
open operator fun contains(element: E): Boolean {
forEach { if (it == element) return true }
return false
}
@Suppress("unused")
open fun containsAll(elements: Collection<E>): Boolean = elements.all { contains(it) }
open fun isEmpty(): Boolean = head.allMatching { it.isValidElementNode().not() }
inline fun forEach(block: (E) -> Unit) {
var node: LockFreeLinkedListNode<E> = head
while (true) {
if (node === tail) return
node.letValueIfValid(block)
node = node.nextNode
}
}
inline fun forEachNode(block: LockFreeLinkedList<E>.(LockFreeLinkedListNode<E>) -> Unit) {
// Copy from forEach
var node: LockFreeLinkedListNode<E> = head
while (true) {
if (node === tail) return
node.letValueIfValid { block(node) }
node = node.nextNode
}
}
@JvmOverloads
@Suppress("unused")
open fun clear(onEach: ((E) -> Unit)? = null) {
val first = head.nextNode
head.nextNode = tail
first.childIterateReturnFirstUnsatisfying(lambda@{
val n = it.nextNode
it.nextNode = tail
it.removed.value = true
if (n === tail) {
return@lambda n
}
onEach?.invoke(n.nodeValue)
n
}, { it !== tail }) // clear the link structure, help GC.
}
@Suppress("unused")
open fun removeAll(elements: Collection<E>): Boolean = elements.all { remove(it) }
@Suppress("DuplicatedCode")
open fun removeNode(node: LockFreeLinkedListNode<E>): Boolean {
if (node == tail) {
return false
}
while (true) {
val before = head.iterateBeforeFirst { it === node }
val toRemove = before.nextNode
if (toRemove === tail) {
return false
}
if (toRemove.isRemoved()) {
continue
}
@Suppress("BooleanLiteralArgument") // false positive
if (!toRemove.removed.compareAndSet(false, true)) {
// logically remove: all the operations will recognize this node invalid
continue
}
// physically remove: try to fix the link
var next: LockFreeLinkedListNode<E> = toRemove.nextNode
while (next !== tail && next.isRemoved()) {
next = next.nextNode
}
if (before.nextNodeRef.compareAndSet(toRemove, next)) {
return true
}
}
}
/*
fun removeAt(index: Int): E {
require(index >= 0) { "index must be >= 0" }
val nodeBeforeIndex = head.iterateValidNodeNTimes(index)
val value = nodeBeforeIndex.nodeValue
if (value === null) noSuchElement()
removeNode(nodeBeforeIndex)
return value
}
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
if (!head.iterateStopOnFirst {
i++
it.nodeValue == element
}.isValidElementNode()) {
return -1
}
return i - 1 // iteration is stopped at the next node
}
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> {
var currentNode: Node<E>
get() = currentNodeRef.value
set(value) {
currentNodeRef.value = value
}
private val currentNodeRef: AtomicRef<Node<E>> = atomic(head) // concurrent compatibility
/**
* Check if
*
* **Notice That:**
* if `hasNext` returned `true`, then the last remaining element is removed concurrently,
* [next] will produce a [NoSuchElementException]
*/
override fun hasNext(): Boolean = !currentNode.iterateStopOnFirst { it.isValidElementNode() }.isTail()
/**
* Iterate until the next node is not
*/
override fun next(): E {
while (true) {
val next = currentNode.nextNode
if (next.isTail()) noSuchElement()
currentNode = next
val nodeValue = next.nodeValue
if (nodeValue != null) { // next node is not removed, that's what we want
return nodeValue
} // or try again
}
}
override fun remove() {
if (!removeNode(currentNode)) { // search from head onto the node, concurrent compatibility
noSuchElement()
}
}
}
*/
}
// region internal
@Suppress("NOTHING_TO_INLINE")
private inline fun <E> E.asNode(nextNode: LockFreeLinkedListNode<E>): LockFreeLinkedListNode<E> =
LockFreeLinkedListNode(nextNode, this)
/**
* Self-iterate using the [iterator], until [mustBeTrue] returns `false`.
* Returns the element at the last time when the [mustBeTrue] returns `true`
*/
@PublishedApi
internal inline fun <N : LockFreeLinkedListNode<*>> N.childIterateReturnsLastSatisfying(
iterator: (N) -> N,
mustBeTrue: (N) -> Boolean
): N {
if (!mustBeTrue(this)) return this
var value: N = this
while (true) {
val newValue = iterator(value)
if (mustBeTrue(newValue)) {
value = newValue
} else {
return value
}
if (newValue is Tail<*>) return newValue
}
}
/**
* Self-iterate using the [iterator], until [mustBeTrue] returns `false`.
* Returns the element at the first time when the [mustBeTrue] returns `false`
*/
private inline fun <E> E.childIterateReturnFirstUnsatisfying(iterator: (E) -> E, mustBeTrue: (E) -> Boolean): E {
if (!mustBeTrue(this)) return this
var value: E = this
while (true) {
val newValue = iterator(value)
if (mustBeTrue(newValue)) {
value = newValue
} else {
return newValue
}
if (newValue is Tail<*>) return newValue
}
}
/**
* Self-iterate using the [iterator], until [mustBeTrue] returns `false`.
* Returns the count of elements being iterated.
*/
private inline fun <E> E.countChildIterate(iterator: (E) -> E, mustBeTrue: (E) -> Boolean): Int {
var count = 0
var value: E = this
if (!mustBeTrue(value)) return count
while (true) {
count++
val newValue = iterator(value)
if (mustBeTrue(newValue)) {
value = newValue
} else {
return count
}
}
}
@PublishedApi
internal class LazyNode<E> @PublishedApi internal constructor(
nextNode: LockFreeLinkedListNode<E>,
private val valueComputer: () -> E
) : LockFreeLinkedListNode<E>(nextNode, null) {
private val initialized = atomic(false)
private val value: AtomicRef<E?> = atomic(null)
override val nodeValue: E
get() {
@Suppress("BooleanLiteralArgument") // false positive warning
if (initialized.compareAndSet(false, true)) { // ensure only one lucky attempt can go into the if
val value = valueComputer()
this.value.value = value
return value // fast path
}
value.loop {
if (it != null) {
return it
}
}
}
}
@PublishedApi
internal class Head<E>(nextNode: LockFreeLinkedListNode<E>) : LockFreeLinkedListNode<E>(nextNode, null) {
override fun toString(): String = "Head"
override val nodeValue: Nothing get() = error("Internal error: trying to get the value of a Head")
}
@PublishedApi
internal open class Tail<E> : LockFreeLinkedListNode<E>(null, null) {
override fun toString(): String = "Tail"
override val nodeValue: Nothing get() = error("Internal error: trying to get the value of a Tail")
}
internal open class LockFreeLinkedListNode<E>(
nextNode: LockFreeLinkedListNode<E>?,
private val initialNodeValue: E?
) {
/*
internal val id: Int = nextId()
companion object {
private val idCount = atomic(0)
internal fun nextId() = idCount.getAndIncrement()
}*/
override fun toString(): String = "$nodeValue"
open val nodeValue: E get() = initialNodeValue ?: error("Internal error: nodeValue is not initialized")
@PublishedApi
internal val removed: AtomicBoolean = atomic(false)
@Suppress("LeakingThis")
internal val nextNodeRef: AtomicRef<LockFreeLinkedListNode<E>> = atomic(nextNode ?: this)
inline fun <R> letValueIfValid(block: (E) -> R): R? {
return this.takeIf { isValidElementNode() }?.nodeValue?.let(block)
}
/**
* Short cut for accessing [nextNodeRef]
*/
@PublishedApi
internal var nextNode: LockFreeLinkedListNode<E>
get() = nextNodeRef.value
set(value) {
nextNodeRef.value = value
}
/**
* Returns the former node of the last node whence [filter] returns true
*/
inline fun iterateBeforeFirst(filter: (LockFreeLinkedListNode<E>) -> Boolean): LockFreeLinkedListNode<E> =
this.childIterateReturnsLastSatisfying({ it.nextNode }, { !filter(it) })
/**
* Check if all the node which is not [Tail] matches the [condition]
*
* Head, which is this, is also being tested.
* [Tail], is not being tested.
*/
inline fun allMatching(condition: (LockFreeLinkedListNode<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]
*
* E.g.: for `head <- 1 <- 2 <- 3 <- tail`, `iterateStopOnNodeValue(2)` returns the node whose value is 1
*/
@Suppress("NOTHING_TO_INLINE")
internal inline fun iterateBeforeNodeValue(element: E): LockFreeLinkedListNode<E> =
this.iterateBeforeFirst { it.isValidElementNode() && it.nodeValue == element }
}
internal fun <E> LockFreeLinkedListNode<E>.isRemoved() = this.removed.value
internal inline fun LockFreeLinkedListNode<*>.isValidElementNode(): Boolean = !isHead() && !isTail() && !isRemoved()
internal inline fun LockFreeLinkedListNode<*>.isHead(): Boolean = this is Head
internal inline fun LockFreeLinkedListNode<*>.isTail(): Boolean = this is Tail
// end region

View File

@ -1,289 +0,0 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("RemoveRedundantBackticks", "NonAsciiCharacters")
package net.mamoe.mirai.utils
import kotlinx.coroutines.*
import net.mamoe.mirai.test.shouldBeEqualTo
import net.mamoe.mirai.test.shouldBeTrue
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@Suppress("UnusedEquals")
@MiraiExperimentalApi
internal class LockFreeLinkedListTest {
@Test
fun addAndGetSingleThreaded() {
val list = LockFreeLinkedList<Int>()
list.addLast(1)
list.addLast(2)
list.addLast(3)
list.addLast(4)
list.size shouldBeEqualTo 4
}
@Test
fun isEmpty() {
val list = LockFreeLinkedList<Int>()
assertTrue { list.isEmpty() }
list.addLast(1)
assertFalse { list.isEmpty() }
}
@Test
fun addAndGetConcurrent() = runBlocking {
//withContext(Dispatchers.Default){
val list = LockFreeLinkedList<Int>()
list.concurrentDo(1000, 10) { addLast(1) }
list.size shouldBeEqualTo 1000 * 10
list.concurrentDo(100, 10) {
remove(1).shouldBeTrue()
}
list.size shouldBeEqualTo 1000 * 10 - 100 * 10
//}
}
@Suppress("UNREACHABLE_CODE", "DeferredResultUnused")
@Test
fun `so many concurrent add remove and foreach`() = runBlocking {
return@runBlocking // 测试通过了, 加快速度. 因为 kotlin 一些其他 bug
val list = LockFreeLinkedList<Int>()
val addJob = async { list.concurrentDo(2, 30000) { addLast(1) } }
//delay(1) // let addJob fly
val foreachJob = async {
list.concurrentDo(1, 10000) {
forEach { it + it }
}
}
val removeLastJob = async {
list.concurrentDo(1, 15000) {
removeLast() shouldBeEqualTo 1
}
}
val removeFirstJob = async {
list.concurrentDo(1, 10000) {
removeFirst() shouldBeEqualTo 1
}
}
val addJob2 = async {
list.concurrentDo(1, 5000) {
addLast(1)
}
}
val removeExactJob = launch {
list.concurrentDo(3, 1000) {
remove(1).shouldBeTrue()
}
}
val filteringGetOrAddJob = launch {
list.concurrentDo(1, 10000) {
filteringGetOrAdd({ it == 2 }, { 1 })
}
}
joinAll(addJob, addJob2, foreachJob, removeLastJob, removeFirstJob, removeExactJob, filteringGetOrAddJob)
list.size shouldBeEqualTo 2 * 30000 - 1 * 15000 - 1 * 10000 + 1 * 5000 - 3 * 1000 + 1 * 10000
}
@Test
fun removeWhileForeach() {
val list = LockFreeLinkedList<Int>()
repeat(10) { list.addLast(it) }
list.forEach {
list.remove(it + 1)
}
list.peekFirst() shouldBeEqualTo 0
}
@Test
fun remove() {
val list = LockFreeLinkedList<Int>()
assertFalse { list.remove(1) }
assertEquals(0, list.size)
list.addLast(1)
assertTrue { list.remove(1) }
assertEquals(0, list.size)
list.addLast(2)
assertFalse { list.remove(1) }
assertEquals(1, list.size)
}
@Test
fun addAll() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
list.size shouldBeEqualTo 5
}
@Test
fun clear() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
list.size shouldBeEqualTo 5
list.clear()
list.size shouldBeEqualTo 0
}
@OptIn(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 `filteringGetOrAdd when add`() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
val value = list.filteringGetOrAdd({ it == 6 }, { 6 })
println("Check value")
value shouldBeEqualTo 6
println("Check size")
// println(list.getLinkStructure())
list.size shouldBeEqualTo 6
}
@Test
fun `filteringGetOrAdd when get`() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
val value = list.filteringGetOrAdd({ it == 2 }, { 2 })
println("Check value")
value shouldBeEqualTo 2
println("Check size")
// println(list.getLinkStructure())
list.size shouldBeEqualTo 5
}
@Test
fun `remove while foreach`() {
val list = LockFreeLinkedList<Int>()
list.addAll(listOf(1, 2, 3, 4, 5))
list.forEach {
list.remove(3)
}
list.toString() shouldBeEqualTo "[1, 2, 4, 5]"
}
@Test
fun `filteringGetOrAdd when empty`() {
val list = LockFreeLinkedList<Int>()
val value = list.filteringGetOrAdd({ it == 2 }, { 2 })
println("Check value")
value shouldBeEqualTo 2
println("Check size")
// println(list.getLinkStructure())
list.size shouldBeEqualTo 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 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
}
*/
}
@OptIn(ExperimentalCoroutinesApi::class)
@MiraiExperimentalApi
internal suspend inline fun <E : LockFreeLinkedList<*>> E.concurrentDo(
numberOfCoroutines: Int,
times: Int,
crossinline todo: E.() -> Unit
) =
coroutineScope {
repeat(numberOfCoroutines) {
launch(start = CoroutineStart.UNDISPATCHED) {
repeat(times) {
todo()
}
}
}
}