Skip to content

Commit e33807b

Browse files
committed
Refactoring and add fine-grained with tests
1 parent 8a5de7a commit e33807b

File tree

10 files changed

+307
-111
lines changed

10 files changed

+307
-111
lines changed

work4/src/main/kotlin/Main.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1+
import kotlinx.coroutines.Job
2+
import kotlinx.coroutines.launch
3+
import kotlinx.coroutines.newSingleThreadContext
14
import kotlinx.coroutines.runBlocking
25
import trees.CoarseGrainedBinaryTree
6+
import trees.FineGrainedBinaryTree
37

48

59
fun main(): Unit = runBlocking {
6-
val tree = CoarseGrainedBinaryTree<Int>()
7-
tree.add(2)
8-
tree.add(1)
9-
tree.add(3)
10-
tree.contains(1)
11-
tree.remove(1)
12-
tree.contains(1)
10+
println("Hello world!")
1311
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package trees
2+
3+
import kotlinx.coroutines.sync.Mutex
4+
5+
abstract class BinarySearchTree<T : Comparable<T>> {
6+
protected var root: Node<T>? = null
7+
protected val treeRootMutex = Mutex()
8+
9+
abstract suspend fun contains(x: T): Boolean
10+
abstract suspend fun add(x: T)
11+
abstract suspend fun remove(x: T)
12+
13+
suspend fun getValues(): List<T> {
14+
val res = mutableListOf<T>()
15+
fun getValuesRecursively(currentNode: Node<T>?) {
16+
currentNode ?: return
17+
getValuesRecursively(currentNode.left)
18+
res.add(currentNode.key)
19+
getValuesRecursively(currentNode.right)
20+
}
21+
treeRootMutex.lock()
22+
getValuesRecursively(root)
23+
treeRootMutex.unlock()
24+
return res
25+
}
26+
}

work4/src/main/kotlin/trees/CoarseGrainedBinaryTree.kt

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ package trees
22

33
import kotlinx.coroutines.sync.Mutex
44

5-
class CoarseGrainedBinaryTree<T : Comparable<T>> {
6-
private var root: Node<T>? = null
7-
private val rootMutex = Mutex()
8-
9-
suspend fun contains(x: T): Boolean {
5+
class CoarseGrainedBinaryTree<T : Comparable<T>> : BinarySearchTree<T>() {
6+
override suspend fun contains(x: T): Boolean {
107
fun containsRecursively(currentNode: Node<T>?): Boolean {
118
currentNode ?: return false
129

@@ -18,13 +15,13 @@ class CoarseGrainedBinaryTree<T : Comparable<T>> {
1815
containsRecursively(currentNode.right)
1916
}
2017
}
21-
rootMutex.lock()
18+
treeRootMutex.lock()
2219
val result = containsRecursively(root)
23-
rootMutex.unlock()
20+
treeRootMutex.unlock()
2421
return result
2522
}
2623

27-
suspend fun add(x: T) {
24+
override suspend fun add(x: T) {
2825
fun addRecursively(currentNode: Node<T>?): Node<T> {
2926
currentNode ?: return Node(x, null, null)
3027

@@ -35,12 +32,12 @@ class CoarseGrainedBinaryTree<T : Comparable<T>> {
3532
}
3633
return currentNode
3734
}
38-
rootMutex.lock()
35+
treeRootMutex.lock()
3936
root = addRecursively(root)
40-
rootMutex.unlock()
37+
treeRootMutex.unlock()
4138
}
4239

43-
suspend fun remove(x: T) {
40+
override suspend fun remove(x: T) {
4441
fun removeNode(node: Node<T>): Node<T>? {
4542
if (node.left != null && node.right != null) {
4643
var curChild = node.left
@@ -68,20 +65,9 @@ class CoarseGrainedBinaryTree<T : Comparable<T>> {
6865
}
6966
return currentNode
7067
}
71-
rootMutex.lock()
72-
root = removeRecursively(root)
73-
rootMutex.unlock()
74-
}
7568

76-
fun getValues(): List<T> {
77-
val res = mutableListOf<T>()
78-
fun getValuesRecursively(currentNode: Node<T>?) {
79-
currentNode ?: return
80-
getValuesRecursively(currentNode.left)
81-
res.add(currentNode.key)
82-
getValuesRecursively(currentNode.right)
83-
}
84-
getValuesRecursively(root)
85-
return res
69+
treeRootMutex.lock()
70+
root = removeRecursively(root)
71+
treeRootMutex.unlock()
8672
}
8773
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package trees
2+
3+
class FineGrainedBinaryTree<T : Comparable<T>> : BinarySearchTree<T>() {
4+
override suspend fun contains(x: T): Boolean {
5+
suspend fun containsRecursively(currentNode: Node<T>): Boolean {
6+
if (x == currentNode.key) {
7+
currentNode.unlock()
8+
return true
9+
} else if (x < currentNode.key) {
10+
if (currentNode.left == null) {
11+
currentNode.unlock()
12+
return false
13+
} else {
14+
currentNode.left!!.lock()
15+
currentNode.unlock()
16+
return containsRecursively(currentNode.left!!)
17+
}
18+
} else {
19+
if (currentNode.right == null) {
20+
currentNode.unlock()
21+
return false
22+
} else {
23+
currentNode.right!!.lock()
24+
currentNode.unlock()
25+
return containsRecursively(currentNode.right!!)
26+
}
27+
}
28+
}
29+
treeRootMutex.lock()
30+
if (root == null) {
31+
treeRootMutex.unlock()
32+
return false
33+
} else {
34+
root!!.lock()
35+
treeRootMutex.unlock()
36+
return containsRecursively(root!!)
37+
}
38+
}
39+
40+
override suspend fun add(x: T) {
41+
suspend fun addRecursively(currentNode: Node<T>) {
42+
if (x == currentNode.key) {
43+
currentNode.unlock()
44+
} else if (x < currentNode.key) {
45+
if (currentNode.left == null) {
46+
currentNode.left = Node(x, null, null)
47+
currentNode.unlock()
48+
} else {
49+
currentNode.left!!.lock()
50+
currentNode.unlock()
51+
addRecursively(currentNode.left!!)
52+
}
53+
} else {
54+
if (currentNode.right == null) {
55+
currentNode.right = Node(x, null, null)
56+
currentNode.unlock()
57+
} else {
58+
currentNode.right!!.lock()
59+
currentNode.unlock()
60+
addRecursively(currentNode.right!!)
61+
}
62+
}
63+
}
64+
treeRootMutex.lock()
65+
if (root == null) {
66+
root = Node(x, null, null)
67+
treeRootMutex.unlock()
68+
} else {
69+
root!!.lock()
70+
treeRootMutex.unlock()
71+
addRecursively(root!!)
72+
}
73+
}
74+
75+
override suspend fun remove(x: T) {
76+
suspend fun findRightSuccessor(node: Node<T>): Node<T> {
77+
node.right?.lock()
78+
if (node.right == null) {
79+
return node
80+
}
81+
var currentNode = node.right!!
82+
while (currentNode.right != null) {
83+
currentNode.right!!.lock()
84+
currentNode.unlock()
85+
currentNode = currentNode.right!!
86+
}
87+
return currentNode
88+
}
89+
90+
/**
91+
* Return locked node
92+
*/
93+
suspend fun removeNode(nodeToRemove: Node<T>): Node<T>? {
94+
nodeToRemove.left?.lock()
95+
nodeToRemove.right?.lock()
96+
val newNode = if (nodeToRemove.left != null && nodeToRemove.right != null) {
97+
val rightSuccessor = findRightSuccessor(nodeToRemove.left!!)
98+
rightSuccessor.right = nodeToRemove.right
99+
nodeToRemove.right?.unlock()
100+
if (rightSuccessor != nodeToRemove.left) rightSuccessor.unlock()
101+
nodeToRemove.left
102+
} else if (nodeToRemove.left == null) {
103+
nodeToRemove.right
104+
} else {
105+
nodeToRemove.left
106+
}
107+
return newNode
108+
}
109+
110+
suspend fun removeRecursively(currentNode: Node<T>, parentNode: Node<T>?, left: Boolean) {
111+
if (x == currentNode.key) {
112+
val newNode = removeNode(currentNode)
113+
if (parentNode == null) {
114+
root = newNode
115+
} else if (left) {
116+
parentNode.left = newNode
117+
} else {
118+
parentNode.right = newNode
119+
}
120+
currentNode.unlock()
121+
newNode?.unlock()
122+
parentNode?.unlock()
123+
} else if (x < currentNode.key) {
124+
currentNode.left?.lock()
125+
parentNode?.unlock()
126+
currentNode.left?.let {
127+
removeRecursively(it, currentNode, true)
128+
} ?: run {
129+
currentNode.unlock()
130+
}
131+
} else {
132+
currentNode.right?.lock()
133+
parentNode?.unlock()
134+
currentNode.right?.let {
135+
removeRecursively(it, currentNode, false)
136+
} ?: run {
137+
currentNode.unlock()
138+
}
139+
}
140+
}
141+
treeRootMutex.lock()
142+
if (root == null) {
143+
treeRootMutex.unlock()
144+
} else {
145+
root!!.lock()
146+
treeRootMutex.unlock()
147+
removeRecursively(root!!, null, true)
148+
}
149+
}
150+
151+
}

work4/src/main/kotlin/trees/Node.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
package trees
22

3+
import kotlinx.coroutines.sync.Mutex
4+
35
class Node<T : Comparable<T>>(
46
var key: T,
57
var left: Node<T>?,
68
var right: Node<T>?
7-
)
9+
) {
10+
private val mutex = Mutex()
11+
12+
suspend fun lock() {
13+
mutex.lock()
14+
}
15+
16+
fun unlock() {
17+
if (mutex.isLocked) mutex.unlock()
18+
}
19+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package trees
2+
3+
import kotlinx.coroutines.*
4+
import org.junit.jupiter.api.Assertions
5+
import org.junit.jupiter.api.BeforeEach
6+
import org.junit.jupiter.params.ParameterizedTest
7+
import org.junit.jupiter.params.provider.Arguments
8+
import org.junit.jupiter.params.provider.MethodSource
9+
import kotlin.random.Random
10+
11+
abstract class BinarySearchTreeTests {
12+
protected lateinit var tree: BinarySearchTree<Int>
13+
private val rnd = Random(0)
14+
15+
@BeforeEach
16+
abstract fun setUp()
17+
18+
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
19+
@ParameterizedTest
20+
@MethodSource("threadNumsProvider")
21+
fun addingValuesTest(threadsNum: Int) {
22+
val valuesToAddLists = List(threadsNum) { List(5000) { rnd.nextInt(5000) } }
23+
runBlocking {
24+
valuesToAddLists.forEachIndexed { id, list ->
25+
launch(newSingleThreadContext("Thread$id")) {
26+
list.forEach { tree.add(it) }
27+
}
28+
}
29+
}
30+
runBlocking {
31+
valuesToAddLists.forEachIndexed { id, list ->
32+
launch(newSingleThreadContext("Thread$id")) {
33+
list.forEach { Assertions.assertTrue(tree.contains(it)) }
34+
}
35+
}
36+
}
37+
}
38+
39+
@OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
40+
@ParameterizedTest
41+
@MethodSource("threadNumsProvider")
42+
fun deletingValuesTest(threadsNum: Int) {
43+
val valuesToRemoveLists = List(threadsNum) { List(5000 ) { rnd.nextInt(5000) } }
44+
valuesToRemoveLists.forEach {
45+
println(it)
46+
}
47+
val jobs = mutableListOf<Job>()
48+
runBlocking {
49+
valuesToRemoveLists.forEachIndexed { id, list ->
50+
launch(newSingleThreadContext("Thread$id")) {
51+
list.forEach { tree.add(it) }
52+
}.let {
53+
jobs.add(it)
54+
}
55+
}
56+
57+
jobs.forEach {
58+
it.join()
59+
}
60+
valuesToRemoveLists.forEachIndexed { id, list ->
61+
launch(newSingleThreadContext("Thread$id")) {
62+
list.forEach { tree.remove(it) }
63+
}
64+
}
65+
}
66+
println("ABOBUS")
67+
runBlocking {
68+
valuesToRemoveLists.forEachIndexed { id, list ->
69+
launch(newSingleThreadContext("Thread$id")) {
70+
list.forEach { Assertions.assertFalse(tree.contains(it)) }
71+
}
72+
}
73+
}
74+
}
75+
76+
companion object {
77+
@JvmStatic
78+
fun threadNumsProvider(): List<Arguments> =
79+
listOf(Arguments.of(1), Arguments.of(2), Arguments.of(4))
80+
}
81+
}

0 commit comments

Comments
 (0)