
ru.hnau.jutils.finisher.Finisher.kt Maven / Gradle / Ivy
The newest version!
package ru.hnau.jutils.finisher
import ru.hnau.jutils.TimeValue
import ru.hnau.jutils.helpers.weak.WeakListener
import ru.hnau.jutils.awaitOrElse
import ru.hnau.jutils.finisher.detacher.ActiveFinisherDetacher
import ru.hnau.jutils.finisher.detacher.FakeFinisherDetacher
import ru.hnau.jutils.finisher.detacher.FinisherDetacher
import ru.hnau.jutils.finisher.detacher.FinisherDetachers
import ru.hnau.jutils.helpers.Box
import ru.hnau.jutils.helpers.Completable
import ru.hnau.jutils.possible.Possible
import ru.hnau.jutils.producer.Producer
import ru.hnau.jutils.producer.detacher.ProducerDetachers
import ru.hnau.jutils.producer.locked_producer.FinishersLockedProducer
import java.util.concurrent.CancellationException
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.CountDownLatch
@Deprecated("Use kotlinx.coroutines.Deferred instead")
open class Finisher(
action: (onFinished: (T) -> Unit) -> Unit
) : Completable {
var result: Box? = null
private set
private val listeners = CopyOnWriteArraySet<(T) -> Unit>()
val finished: Boolean
get() = result != null
init {
action.invoke(::onResultReceived)
}
fun applyAwait(listener: (T) -> Unit) =
apply { await(listener) }
fun addToLocker(lockedProducer: FinishersLockedProducer) =
apply(lockedProducer::waitFinisher)
private fun onResultReceived(result: T) = synchronized(this) {
if (finished) {
return
}
this.result = Box(result)
listeners.forEach { it.invoke(result) }
listeners.clear()
}
fun await(listener: (T) -> Unit): FinisherDetacher {
val result = this.result
if (result != null) {
listener.invoke(result.value)
return FakeFinisherDetacher()
}
listeners.add(listener)
return ActiveFinisherDetacher(this, listener)
}
fun await(detacher: FinisherDetachers, listener: (T) -> Unit) =
detacher.addDetacher(await(listener))
fun weakAwait(listener: (T) -> Unit) = await(
WeakListener(
innerListener = listener,
onReferenceEmpty = this::detach
)
)
fun weakAwait(detacher: FinisherDetachers, listener: (T) -> Unit) =
detacher.addDetacher(weakAwait(listener))
fun detach(listener: (T) -> Unit) {
listeners.remove(listener)
}
fun awaitSync(): T {
val existenceResult = result
if (existenceResult != null) {
return existenceResult.value
}
val latch = CountDownLatch(1)
await { latch.countDown() }
latch.await()
val result = this.result
?: throw IllegalStateException("Unexpected null result")
return result.value
}
override fun executeOnCompleted(onCompletedListener: (T) -> Unit) {
await(onCompletedListener)
}
fun fitInTimeOrElse(
timeOut: TimeValue,
elseAction: (CancellationException) -> T
) = fitInTimeOrElse(
timeOut = timeOut,
fitInTimeAction = { it },
elseAction = elseAction
)
fun fitInTimeOrElse(
timeOut: TimeValue,
fitInTimeAction: (T) -> R,
elseAction: (CancellationException) -> R
): Finisher = NewThreadFinisher { onFinished ->
val countDownLatch = CountDownLatch(1)
await {
countDownLatch.countDown()
val result = fitInTimeAction.invoke(it)
onFinished.invoke(result)
}
countDownLatch.awaitOrElse(timeOut) {
val ex = CancellationException("Fit in time is over (${timeOut.toMillisecondsString()})")
val result = elseAction.invoke(ex)
onFinished.invoke(result)
}
}
@Throws(CancellationException::class)
fun fitInTimeOrThrow(timeOut: TimeValue) = fitInTimeOrElse(
timeOut = timeOut,
elseAction = { throw it }
)
override fun toString() =
"Finisher{${result?.let { "result=$it" }
?: "finished=false, listeners=${listeners.size}"}}"
/**
* Additional methods
*/
companion object {
inline fun sync(action: () -> T) =
forExistenceData(action.invoke())
fun forExistenceData(data: T) =
Finisher { onFinished -> onFinished.invoke(data) }
fun auto(): Finisher = AutoFinisher.create()
fun fromProducer(
producer: Producer,
resultFilter: ((T) -> Boolean)? = null
) =
Finisher { onFinished ->
val detachers = ProducerDetachers()
producer.attach(detachers) { producedData ->
if (resultFilter == null || resultFilter.invoke(producedData)) {
detachers.detachAllAndClear()
onFinished.invoke(producedData)
}
}
}
fun fromProducerOrElseIfTimeout(
producer: Producer,
timeOut: TimeValue,
converter: (T) -> R,
timeoutAction: (CancellationException) -> R,
resultFilter: ((T) -> Boolean)? = null
) =
NewThreadFinisher { onFinished ->
val lock = Any()
val detachers = ProducerDetachers()
val countDownLatch = CountDownLatch(1)
producer.attach(detachers) { producedData ->
synchronized(lock) {
if (resultFilter == null || resultFilter.invoke(producedData)) {
detachers.detachAllAndClear()
countDownLatch.countDown()
onFinished.invoke(converter.invoke(producedData))
}
}
}
countDownLatch.awaitOrElse(timeOut) {
synchronized(lock) {
detachers.detachAllAndClear()
val ex = CancellationException("Fit in time is over (${timeOut.toMillisecondsString()})")
onFinished.invoke(timeoutAction.invoke(ex))
}
}
}
fun fromProducerOrPossibleErrorIfTimeout(
producer: Producer,
timeOut: TimeValue,
resultFilter: ((T) -> Boolean)? = null
) = fromProducerOrElseIfTimeout(
producer = producer,
converter = { Possible.success(it) },
timeoutAction = { Possible.error(it) },
resultFilter = resultFilter,
timeOut = timeOut
)
fun fromProducerOrThrowIfTimeout(
producer: Producer,
timeOut: TimeValue,
resultFilter: ((T) -> Boolean)? = null
) = fromProducerOrElseIfTimeout(
producer = producer,
converter = { it },
timeoutAction = { throw IllegalStateException(it) },
resultFilter = resultFilter,
timeOut = timeOut
)
}
fun mapAsync(additionalAction: (data: T, onFinished: (O) -> Unit) -> Unit): Finisher =
Finisher { onFinished ->
await { data ->
additionalAction.invoke(data, onFinished)
}
}
fun map(converter: (T) -> O): Finisher =
mapAsync { data, onFinished ->
val result = converter.invoke(data)
onFinished.invoke(result)
}
fun map(value: O) = map { value }
fun mapToUnit() = map(Unit)
fun attach(other: Finisher): Finisher> =
Finisher { onFinished ->
this.await { thisResult ->
other.await { otherResult ->
onFinished.invoke(thisResult to otherResult)
}
}
}
operator fun plus(other: Finisher) = this.attach(other)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy