org.jetbrains.kotlinx.jupyter.streams.SubstitutionEngine.kt Maven / Gradle / Ivy
The newest version!
package org.jetbrains.kotlinx.jupyter.streams
import org.jetbrains.kotlinx.jupyter.api.StreamSubstitutionType
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/**
* Creates new data using possibly existing initial data
*/
typealias DataFactory = (initialData: DataT?) -> DataT
/**
* Substitutes the shared data property with the `newData`. Returns previous value of the property
*/
typealias DataSubstitutor = (newData: DataT) -> DataT
/**
* Substitutes old data back to the property. Possibly destroys newData as long it's no longer necessary
*/
typealias DataFinalizer = (newData: DataT, oldData: DataT) -> Unit
/**
* Parameters of [SubstitutionEngine]
*/
class DataFlowComponents(
val initialData: DataT,
val substitutor: DataSubstitutor,
val finalizer: DataFinalizer,
)
/**
* Handles the logic of how data is created, when it's substituted instead of the previous value
* of a shared resource, and how it's substituted back
*/
abstract class SubstitutionEngine(
dataFlowComponents: DataFlowComponents,
) {
protected val initialData: DataT = dataFlowComponents.initialData
protected val substitutor: DataSubstitutor = dataFlowComponents.substitutor
protected val finalizer: DataFinalizer = dataFlowComponents.finalizer
/**
* Substitutes the data provided by [dataFactory] instead of a shared resource
* (which is done by [substitutor]), executes [body] in this context and calls
* [finalizer] afterwards. Note that [finalizer] is called even in the case of exceptional
* completion of [body].
*/
abstract fun withDataSubstitution(
dataFactory: DataFactory,
body: () -> T,
): T
}
/**
* Implements the logic of substitution for [StreamSubstitutionType.BLOCKING]
*/
class BlockingSubstitutionEngine(
dataFlowComponents: DataFlowComponents,
) : SubstitutionEngine(dataFlowComponents) {
private val lock = ReentrantLock()
override fun withDataSubstitution(
dataFactory: DataFactory,
body: () -> T,
): T {
val newData = dataFactory(initialData)
return lock.withLock {
val oldData = substitutor(newData)
try {
body()
} finally {
finalizer(newData, oldData)
}
}
}
}
/**
* Implements the logic of substitution for [StreamSubstitutionType.NON_BLOCKING]
*/
class NonBlockingSubstitutionEngine(
dataFlowComponents: DataFlowComponents,
) : SubstitutionEngine(dataFlowComponents) {
private val lock = ReentrantLock()
private val dataList = DataList(initialData)
override fun withDataSubstitution(
dataFactory: DataFactory,
body: () -> T,
): T {
val newData =
lock.withLock {
val myInitialData = dataList.last()
val data = dataFactory(myInitialData)
substitutor(data).also { _ ->
dataList.add(data)
}
data
}
return try {
body()
} finally {
lock.withLock {
with(dataList.remove(newData)) {
if (wasLast) {
finalizer(newData, newLast)
}
}
}
}
}
}