Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Mirai
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Mirai
Commits
a1d3cf0f
Commit
a1d3cf0f
authored
Dec 14, 2019
by
Him188
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
LockFreeLinkedList
parent
4b28f004
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
411 additions
and
314 deletions
+411
-314
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
...onMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
+319
-172
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
...st/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
+92
-142
No files found.
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/LockFreeLinkedList.kt
View file @
a1d3cf0f
...
@@ -6,67 +6,104 @@ import kotlinx.atomicfu.AtomicRef
...
@@ -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
}
}
}
}
}
override
fun
toString
():
String
=
"["
+
buildString
{
this
@LockFreeLinkedList
.
forEach
{
append
(
it
)
append
(
", "
)
}
}
}.
dropLast
(
2
)
+
"]"
internal
fun
getLinkStucture
():
String
=
buildString
{
@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 {
...
@@ -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
// log
a
ically 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,74 +123,24 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
...
@@ -86,74 +123,24 @@ 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
{
var
i
=
-
1
// head
if
(!
head
.
iterateStopOnFirst
{
i
++
it
.
nodeValueRef
.
value
==
element
}.
isValidElementNode
())
{
return
-
1
}
return
i
-
1
// iteration is stopped at the next node
}
override
fun
isEmpty
():
Boolean
=
head
.
allMatching
{
it
.
nodeValueRef
.
value
==
null
}
/**
* Create a concurrent-unsafe iterator
*/
override
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
operator fun set(index: Int, element: E): E {
/**
* 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) {
while (true) {
val
next
=
currentNode
.
nextNode
val nodeAtIndex = head.iterateValidNodeNTimes(index + 1)
if
(
next
.
isTail
())
noSuchElement
()
val originalValue = nodeAtIndex.nodeValue
if (originalValue === null) noSuchElement() // this node has been removed.
currentNode
=
next
if (!nodeAtIndex.nodeValueRef.compareAndSet(null, element)) { // with concurrent compatibility
continue
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
()
}
}
return originalValue
}
}
}
}
...
@@ -169,10 +156,8 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
...
@@ -169,10 +156,8 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
* 4. Repeat 2,3 until the `tail` is reached.
* 4. Repeat 2,3 until the `tail` is reached.
*
*
* Concurrent changes may influence the result.
* Concurrent changes may influence the result.
* While searching,
*
*/
*/
override
fun
lastIndexOf
(
element
:
E
):
Int
{
fun lastIndexOf(element: E): Int {
var lastMatching: Node<E> = head
var lastMatching: Node<E> = head
var searchStartingFrom: Node<E> = lastMatching
var searchStartingFrom: Node<E> = lastMatching
var index = 0 // accumulated index from each search
var index = 0 // accumulated index from each search
...
@@ -188,7 +173,7 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
...
@@ -188,7 +173,7 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
searchStartingFrom = got.nextNode
searchStartingFrom = got.nextNode
index += timesOnThisTurn
index += timesOnThisTurn
if
(!
got
.
isRemoved
())
lastMatching
=
got
//record the lastMatching only if got is not removed.
if (!got.isRemoved()) lastMatching = got
}
}
if (!lastMatching.isValidElementNode()) {
if (!lastMatching.isValidElementNode()) {
...
@@ -198,61 +183,257 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
...
@@ -198,61 +183,257 @@ class LockFreeLinkedList<E> : MutableList<E>, RandomAccess {
return index
return index
}
}
*/
/*
override fun listIterator(): MutableListIterator<E> = listIterator0(0)
override fun listIterator(): MutableListIterator<E> = listIterator0(0)
override fun listIterator(index: Int): MutableListIterator<E> = listIterator0(index)
override fun listIterator(index: Int): MutableListIterator<E> = listIterator0(index)
@Suppress("NOTHING_TO_INLINE")
@Suppress("NOTHING_TO_INLINE")
internal
inline
fun
listIterator0
(
index
:
Int
):
MutableListIterator
<
E
>
{
private inline fun listIterator0(index: Int): MutableListIterator<E> {
TODO
()
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
}
}
override
fun
subList
(
fromIndex
:
Int
,
toIndex
:
Int
):
MutableList
<
E
>
{
private val currentNodeRef: AtomicRef<Node<E>> = atomic(first) // concurrent compatibility
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
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
}
override
fun
add
(
index
:
Int
,
element
:
E
)
{
if (!foundNode.isValidElementNode()) {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
// Current node is not found in the list, meaning it had been removed concurrently
previousNode = head
previousNodeIndexRef.value = -1
return false
}
}
override
fun
addAll
(
index
:
Int
,
elements
:
Collection
<
E
>):
Boolean
{
if (lastSatisfying === null) {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
// All the previous nodes are logically removed.
previousNode = head
previousNodeIndexRef.value = -1
return false
}
}
override
fun
addAll
(
elements
:
Collection
<
E
>):
Boolean
{
currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value = localNodeAtTheMomentWhenPreviousNodeIsUpdated
elements
.
forEach
{
add
(
it
)
}
previousNode = lastSatisfying!! // false positive nullable warning
previousNodeIndexRef.value = i
return true
return true
}
}
}
override
fun
clear
()
{
override fun previous(): E {
head
.
nextNode
=
tail
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()
}
}
// TODO: 2019/12/13 check ?
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
removeAll
(
elements
:
Collection
<
E
>):
Boolean
{
override fun add(element: E) {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
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
removeAt
(
index
:
Int
):
E
{
override fun hasNext(): Boolean {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
if (currentNodeAtTheMomentWhenPreviousNodeIsUpdated.value == head) {
// node list have been changed.
if (!updatePrevious()) noSuchElement()
}
return currentNode.nextNode !== tail
}
}
override
fun
retainAll
(
elements
:
Collection
<
E
>):
Boolean
{
override fun next(): E {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
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
operator
fun
set
(
index
:
Int
,
element
:
E
):
E
{
override fun remove() {
TODO
(
"not implemented"
)
//To change body of created functions use File | Settings | File Templates.
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()
}
// NO INLINE: currently exceptions thrown in a inline function cannot be traced
}
private
fun
noSuchElement
():
Nothing
=
throw
NoSuchElementException
()
}
}
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()
}
}
}
*/
}
/**
* 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: (
...
@@ -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
.
childIterateReturnFirstUns
i
tisfying
(
iterator
:
(
E
)
->
E
,
mustBeTrue
:
(
E
)
->
Boolean
):
E
{
private
inline
fun
<
E
>
E
.
childIterateReturnFirstUns
a
tisfying
(
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) -
...
@@ -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>(
...
@@ -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>(
...
@@ -360,38 +544,10 @@ private open class Node<E>(
/**
/**
* Returns the former node of the last node whence [filter] return
e
s 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>(
...
@@ -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>(
...
@@ -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
...
@@ -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
\ No newline at end of file
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt
View file @
a1d3cf0f
...
@@ -4,17 +4,18 @@ package net.mamoe.mirai.utils
...
@@ -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
(
5
000
)
delay
(
30
*
1
000
)
exitProcess
(-
100
)
exitProcess
(-
100
)
}
}
}
}
...
@@ -27,26 +28,41 @@ internal class LockFreeLinkedListTest {
...
@@ -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
)
{
list
.
concurrentDo
(
100
,
10
)
{
remove
(
1
)
remove
(
1
).
shouldBeTrue
()
}
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"
)
}
}
runBlocking
{
list
.
concurrentDo
(
1000
,
10
)
{
joinAll
(
add
,
remove
)
remove
(
1
).
shouldBeTrue
(
)
}
}
assertEquals
(
1000
*
10
-
100
*
10
,
list
.
size
)
addJob
.
join
()
list
.
size
shouldBeEqualTo
5000
*
10
-
1000
*
10
}
}
@Test
@Test
...
@@ -66,37 +82,32 @@ internal class LockFreeLinkedListTest {
...
@@ -66,37 +82,32 @@ internal class LockFreeLinkedListTest {
}
}
@Test
@Test
fun
getSize
()
{
fun
addAll
()
{
val
list
=
lockFreeLinkedListOf
(
1
,
2
,
3
,
4
,
5
)
val
list
=
LockFreeLinkedList
<
Int
>()
assertEquals
(
5
,
list
.
size
)
list
.
addAll
(
listOf
(
1
,
2
,
3
,
4
,
5
))
list
.
size
shouldBeEqualTo
5
val
list2
=
lockFreeLinkedListOf
<
Unit
>()
assertEquals
(
0
,
list2
.
size
)
}
}
@Test
@Test
fun
c
ontains
()
{
fun
c
lear
()
{
val
list
=
lockFreeLinkedListOf
<
Int
>()
val
list
=
LockFreeLinkedList
<
Int
>()
assertFalse
{
list
.
contains
(
0
)
}
list
.
addAll
(
listOf
(
1
,
2
,
3
,
4
,
5
))
list
.
size
shouldBeEqualTo
5
list
.
add
(
0
)
list
.
clear
(
)
assertTrue
{
list
.
contains
(
0
)
}
list
.
size
shouldBeEqualTo
0
}
}
@UseExperimental
(
ExperimentalUnsignedTypes
::
class
)
@Test
@Test
fun
containsAll
()
{
fun
withInlineClassElements
()
{
va
r
list
=
lockFreeLinkedListOf
(
1
,
2
,
3
)
va
l
list
=
LockFreeLinkedList
<
UInt
>(
)
assertTrue
{
list
.
containsAll
(
listOf
(
1
,
2
,
3
))
}
list
.
addAll
(
listOf
(
1
u
,
2
u
,
3
u
,
4
u
,
5
u
))
assertTrue
{
list
.
containsAll
(
listOf
())
}
list
.
size
shouldBeEqualTo
5
list
=
lockFreeLinkedListOf
(
1
,
2
)
list
.
toString
()
shouldBeEqualTo
"[1, 2, 3, 4, 5]"
assertFalse
{
list
.
containsAll
(
listOf
(
1
,
2
,
3
))
}
list
=
lockFreeLinkedListOf
()
assertTrue
{
list
.
containsAll
(
listOf
())
}
assertFalse
{
list
.
containsAll
(
listOf
(
1
))
}
}
}
/*
@Test
@Test
fun indexOf() {
fun indexOf() {
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 3, 3)
val list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(1, 2, 3, 3)
...
@@ -106,15 +117,6 @@ internal class LockFreeLinkedListTest {
...
@@ -106,15 +117,6 @@ internal class LockFreeLinkedListTest {
assertEquals(-1, list.indexOf(4))
assertEquals(-1, list.indexOf(4))
}
}
@Test
fun
isEmpty
()
{
val
list
:
LockFreeLinkedList
<
Int
>
=
lockFreeLinkedListOf
()
list
.
isEmpty
().
shouldBeTrue
()
list
.
add
(
1
)
list
.
isEmpty
().
shouldBeFalse
()
}
@Test
@Test
fun iterator() {
fun iterator() {
var list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
var list: LockFreeLinkedList<Int> = lockFreeLinkedListOf(2)
...
@@ -171,68 +173,16 @@ internal class LockFreeLinkedListTest {
...
@@ -171,68 +173,16 @@ internal class LockFreeLinkedListTest {
list.lastIndexOf(4) shouldBeEqualTo 4
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
fun
addAll
()
{
}
@Test
fun
testAddAll
()
{
}
@Test
fun
clear
()
{
}
@Test
fun
removeAll
()
{
}
@Test
fun
removeAt
()
{
}
@Test
fun
retainAll
()
{
}
@Test
fun
set
()
{
}
}
}
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
)
=
coroutineScope
{
repeat
(
numberOfCoroutines
)
{
repeat
(
numberOfCoroutines
)
{
launch
{
launch
{
repeat
(
timesOfAdd
)
{
repeat
(
timesOfAdd
)
{
...
@@ -240,4 +190,4 @@ internal suspend fun <E : LockFreeLinkedList<*>> E.concurrentDo(numberOfCoroutin
...
@@ -240,4 +190,4 @@ internal suspend fun <E : LockFreeLinkedList<*>> E.concurrentDo(numberOfCoroutin
}
}
}
}
}
}
}
}
\ No newline at end of file
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment