-
Notifications
You must be signed in to change notification settings - Fork 0
/
ThreadSafeContainer.kt
51 lines (47 loc) · 2.15 KB
/
ThreadSafeContainer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package pt.isel.pc.problemsets.set2
import java.util.concurrent.atomic.AtomicInteger
/**
* A thread-safe container that allows multiple threads to consume its values using the [consume] method.
* The container is initialized with a set [AtomicConsumableValue]s, each with a number of lives, that represents the number of times
* that value can be consumed by a thread.
* @param T the type of [AtomicConsumableValue]s to be consumed.
* @param values an array of [AtomicConsumableValue]s.
* @throws IllegalArgumentException if [values] is empty.
*/
class ThreadSafeContainer<T>(private val values: Array<AtomicConsumableValue<T>>) {
private val index = AtomicInteger(0)
init {
require(values.isNotEmpty()) { "values cannot be empty" }
}
/**
* Consumes a value from the [values] container.
* @return the consumed value or null if there are no more values to consume.
*/
fun consume(): T? {
val firstObservedIndex = index.get()
// fast-path -> there are no more values to be consumed
if (firstObservedIndex == values.size) return null
// retry-path -> retrive an index or retry if not possible
while (true) {
val observedIndex = index.get()
// if there are more values to be consumed
if (observedIndex in values.indices) {
val atomicValue = values[observedIndex]
while (true) {
// retry-path -> A thread tries to decrement a life from a value or retries if not possible
val observedLives = atomicValue.lives.get()
val nextLives = if (observedLives > 0) observedLives - 1 else break
if (atomicValue.lives.compareAndSet(observedLives, nextLives)) {
// if the decrement was successful, return the value
return atomicValue.value
}
// retry
}
}
val nextIndex = if (observedIndex < values.size) observedIndex + 1 else break
index.compareAndSet(observedIndex, nextIndex)
// retry
}
return null
}
}