@@ -2,40 +2,71 @@ package dev.helight.krescent.test
22
33import dev.helight.krescent.synchronization.KrescentLockProvider
44import dev.helight.krescent.synchronization.KrescentLockProvider.Extensions.getMultiLock
5- import dev.helight.krescent.synchronization.LocalLockProvider
6- import kotlinx.coroutines.async
7- import kotlinx.coroutines.awaitAll
8- import kotlinx.coroutines.delay
9- import kotlinx.coroutines.runBlocking
5+ import kotlinx.coroutines.*
6+ import org.junit.jupiter.api.Assertions.assertEquals
7+ import java.util.concurrent.atomic.AtomicInteger
108import kotlin.test.Test
119import kotlin.test.assertTrue
10+ import kotlin.time.DurationUnit
11+ import kotlin.time.toDuration
1212
1313interface KrescentLockProviderContract {
1414
1515 fun getProvider (): KrescentLockProvider
16+ val latency: Long
17+ get() = 50L
1618
1719 @Test
1820 fun `Acquired locks work as expected` () = runBlocking {
1921 val provider = getProvider()
20- val timeStart = System .currentTimeMillis()
22+ System .currentTimeMillis()
23+ val resource = LockResource ()
2124 listOf (
2225 async {
2326 val lock = provider.getLock(" test-identity" )
2427 lock.runGuarded {
25- delay(50 )
28+ resource.claim()
29+ delay(latency)
30+ resource.release()
2631 }
2732 },
2833 async {
2934 val lock = provider.getLock(" test-identity" )
3035 lock.runGuarded {
31- delay(50 )
36+ resource.claim()
37+ delay(latency)
38+ resource.release()
3239 }
3340 }
3441 ).awaitAll()
35- val timeAfter = System .currentTimeMillis()
36- assertTrue(timeAfter - timeStart >= 95 )
42+ return @runBlocking
3743 }
3844
45+ @Test
46+ fun `Reused lock instance still works and is not reentrant` () = runBlocking {
47+ val provider = getProvider()
48+ val resource = LockResource ()
49+ val lock = provider.getLock(" test-identity" )
50+ listOf (
51+ async {
52+ lock.runGuarded {
53+ resource.claim()
54+ delay(latency)
55+ resource.release()
56+ }
57+ },
58+ async {
59+ lock.runGuarded {
60+ resource.claim()
61+ delay(latency)
62+ resource.release()
63+ }
64+ }
65+ ).awaitAll()
66+ return @runBlocking
67+ }
68+
69+
3970 @Test
4071 fun `Locks do not conflict with each other` () = runBlocking {
4172 val provider = getProvider()
@@ -44,51 +75,96 @@ interface KrescentLockProviderContract {
4475 async {
4576 val lock = provider.getLock(" test-identity" )
4677 lock.runGuarded {
47- delay(50 )
78+ delay(latency )
4879 }
4980 },
5081 async {
5182 val lock = provider.getLock(" another-identity" )
5283 lock.runGuarded {
53- delay(50 )
84+ delay(latency )
5485 }
55- }
86+ },
5687 ).awaitAll()
5788 val timeAfter = System .currentTimeMillis()
58- assertTrue(timeAfter - timeStart < 95 )
89+ assertTrue(timeAfter - timeStart < latency * 2 )
90+ }
91+
92+ @Test
93+ fun `Test lock timeouts` () = runBlocking {
94+ val provider = getProvider()
95+ val lock = provider.getLock(" timeout-identity" )
96+ lock.acquire()
97+ var timeoutThrown = false
98+ withTimeout(1000L ) {
99+ try {
100+ lock.acquire(latency.toDuration(DurationUnit .MILLISECONDS ))
101+ } catch (e: Exception ) {
102+ timeoutThrown = true
103+ }
104+ println (" Acquired lock after timeout period" )
105+ }
106+ assertTrue(timeoutThrown, " Timeout was not thrown when expected" )
59107 }
60108
61109 @Test
62110 fun `Multilocks work and do not overblock` () = runBlocking {
63- val provider = LocalLockProvider ()
64- val timeStart = System .currentTimeMillis()
111+ val provider = getProvider()
112+ val aRes = LockResource ()
113+ val bRes = LockResource ()
114+ val cRes = LockResource ()
115+ val dRes = LockResource ()
65116 listOf (
66117 async {
67118 val lock = provider.getMultiLock(" a" , " b" )
68119 lock.runGuarded {
69- delay(50 )
120+ aRes.claim()
121+ bRes.claim()
122+ delay(latency)
123+ bRes.release()
124+ aRes.release()
70125 }
71126 },
72127 async {
73128 val lock = provider.getMultiLock(" a" , " c" )
74129 lock.runGuarded {
75- delay(50 )
130+ aRes.claim()
131+ cRes.claim()
132+ delay(latency)
133+ cRes.release()
134+ aRes.release()
76135 }
77136 },
78137 async {
79138 val lock = provider.getMultiLock(" d" , " b" )
80139 lock.runGuarded {
81- delay(50 )
140+ dRes.claim()
141+ bRes.claim()
142+ delay(latency)
143+ bRes.release()
144+ dRes.release()
82145 }
83146 },
84147 async {
85148 val lock = provider.getMultiLock(" x" , " y" )
86149 lock.runGuarded {
87- delay(50 )
150+ delay(latency )
88151 }
89152 },
90153 ).awaitAll()
91- val timeAfter = System .currentTimeMillis()
92- assertTrue(timeAfter - timeStart >= 95 && timeAfter - timeStart <= 150 )
154+ return @runBlocking
155+ }
156+
157+ class LockResource {
158+ private val atomic: AtomicInteger = AtomicInteger (1 )
159+
160+ fun claim () {
161+ val v = atomic.decrementAndGet()
162+ assertEquals(v, 0 , " Resource is already claimed!" )
163+ }
164+
165+ fun release () {
166+ val v = atomic.incrementAndGet()
167+ assertEquals(v, 1 , " Resource was not claimed!" )
168+ }
93169 }
94170}
0 commit comments