Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ kotlin {
}
}

// explicitApi = ExplicitApiMode.Warning
explicitApi()
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
freeCompilerArgs.add("-opt-in=kotlinx.cinterop.BetaInteropApi")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public interface StatusListener : CoroutineContext.Element {
* @param status The reactive status of the process
* @return A [Release] lambda to stop listening to the process
*/
fun watchBackgroundProcess(status: Reactive<*>): Release
public fun watchBackgroundProcess(status: Reactive<*>): Release

/**
* Called when a reactive calculation is happening in the foreground, so this listener can respond accordingly.
Expand All @@ -55,7 +55,7 @@ public interface StatusListener : CoroutineContext.Element {
* @param status The reactive status of the process
* @return A [Release] lambda to stop listening to the process
*/
fun watchForegroundProcess(status: Reactive<*>): Release = watchBackgroundProcess(status)
public fun watchForegroundProcess(status: Reactive<*>): Release = watchBackgroundProcess(status)
}

/**
Expand All @@ -76,7 +76,7 @@ public interface StatusListener : CoroutineContext.Element {
*
* @param action The cleanup action to execute when the scope completes
*/
fun CoroutineScope.onRemove(action: () -> Unit) {
public fun CoroutineScope.onRemove(action: () -> Unit) {
coroutineContext[CoroutineName.Key]
this.coroutineContext[Job]?.invokeOnCompletion { action() }
}
Expand Down Expand Up @@ -221,7 +221,7 @@ fun CoroutineScope.onRemove(action: () -> Unit) {
public sealed interface ReactiveCoroutineScope : CoroutineScope

@Deprecated("No longer needed", ReplaceWith("CoroutineScope"))
typealias CalculationContext = CoroutineScope
public typealias CalculationContext = CoroutineScope

/**
* Checks whether this [CoroutineScope]'s dispatcher is a main thread dispatcher.
Expand All @@ -238,7 +238,7 @@ typealias CalculationContext = CoroutineScope
* ```
*/
@OptIn(ExperimentalStdlibApi::class)
val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[CoroutineDispatcher.Key] is MainCoroutineDispatcher
public val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[CoroutineDispatcher.Key] is MainCoroutineDispatcher

/**
* Executes the given [action] on the thread associated with this [CoroutineScope]'s dispatcher.
Expand Down Expand Up @@ -277,7 +277,7 @@ val CoroutineScope.requireMainThread: Boolean get() = coroutineContext[Coroutine
* @param action The action to execute on this scope's thread
*/
@OptIn(ExperimentalStdlibApi::class)
fun CoroutineScope.onThread(action: () -> Unit) {
public fun CoroutineScope.onThread(action: () -> Unit) {
val d = coroutineContext[CoroutineDispatcher.Key] ?: return action()
if (d.isDispatchNeeded(coroutineContext)) {
d.dispatch(coroutineContext, Runnable(action))
Expand All @@ -293,4 +293,4 @@ fun CoroutineScope.onThread(action: () -> Unit) {
* helping prevent accidental nesting of reactive contexts and providing better IDE support.
*/
@DslMarker
annotation class ReactiveDsl
public annotation class ReactiveDsl
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import kotlin.time.Duration.Companion.milliseconds
* Interface for helper functions which require an additional [CoroutineScope] context. This will eventually
* be removed in favor of context receivers.
* */
interface CoroutineScopeHelpers : CoroutineScope {
public interface CoroutineScopeHelpers : CoroutineScope {
@ReactiveDsl
operator fun <T, IGNORED> ((T) -> IGNORED).invoke(actionToCalculate: ReactiveContext.() -> T) =
public operator fun <T, IGNORED> ((T) -> IGNORED).invoke(actionToCalculate: ReactiveContext.() -> T): TypedReactiveContext<IGNORED> =
this@CoroutineScopeHelpers.reactive(action = { this@invoke(actionToCalculate(this)) })

/**
Expand All @@ -46,7 +46,7 @@ interface CoroutineScopeHelpers : CoroutineScope {
* ```
* */
@ReactiveDsl
operator fun <T> KMutableProperty0<T>.invoke(actionToCalculate: ReactiveContext.() -> T) = this@CoroutineScopeHelpers.reactive(action = { set(actionToCalculate(this)) })
public operator fun <T> KMutableProperty0<T>.invoke(actionToCalculate: ReactiveContext.() -> T): TypedReactiveContext<Unit> = this@CoroutineScopeHelpers.reactive(action = { set(actionToCalculate(this)) })


/**
Expand All @@ -69,7 +69,7 @@ interface CoroutineScopeHelpers : CoroutineScope {
* actually optimize and cut out the overhead of a full `reactive` context.
* */
@ReactiveDsl
infix fun <T> KMutableProperty0<T>.bind(reactive: Reactive<T>) {
public infix fun <T> KMutableProperty0<T>.bind(reactive: Reactive<T>) {
if (reactive is ReactiveValue<T>) { // I did benchmarks, this is just as fast as overloading and easier to use.
val release = reactive.addAndRunListener { this@bind.set(reactive.value) }
// no need for status listener since result is infallible
Expand All @@ -89,7 +89,7 @@ interface CoroutineScopeHelpers : CoroutineScope {
* Changes to either reactive value will propagate to the other.
*/
@ReactiveDsl
infix fun <T> MutableReactive<T>.bind(master: MutableReactive<T>) {
public infix fun <T> MutableReactive<T>.bind(master: MutableReactive<T>) {
val reportTo = RawReactive(ReactiveState(Unit))
coroutineContext[StatusListener]?.watchBackgroundProcess(reportTo)
launch {
Expand Down Expand Up @@ -127,25 +127,25 @@ interface CoroutineScopeHelpers : CoroutineScope {
* Debounces listener notifications by [timeMs] milliseconds using this scope. State is always current.
* @see DebounceReactive
*/
fun <T> Reactive<T>.debounce(timeMs: Long): Reactive<T> = DebounceReactive(this, this@CoroutineScopeHelpers, timeMs.milliseconds)
public fun <T> Reactive<T>.debounce(timeMs: Long): Reactive<T> = DebounceReactive(this, this@CoroutineScopeHelpers, timeMs.milliseconds)

/**
* Debounces listener notifications by [duration] using this scope. State is always current.
* @see DebounceReactive
*/
fun <T> Reactive<T>.debounce(duration: Duration): Reactive<T> = DebounceReactive(this, this@CoroutineScopeHelpers, duration)
public fun <T> Reactive<T>.debounce(duration: Duration): Reactive<T> = DebounceReactive(this, this@CoroutineScopeHelpers, duration)

/**
* Debounces listener notifications by [timeMs] milliseconds using this scope.
* @see DebounceListenable
*/
fun Listenable.debounce(timeMs: Long): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, timeMs.milliseconds)
public fun Listenable.debounce(timeMs: Long): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, timeMs.milliseconds)

/**
* Debounces listener notifications by [duration] using this scope.
* @see DebounceListenable
*/
fun Listenable.debounce(duration: Duration): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, duration)
public fun Listenable.debounce(duration: Duration): Listenable = DebounceListenable(this, this@CoroutineScopeHelpers, duration)
}

@OptIn(ExperimentalStdlibApi::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import kotlin.coroutines.resumeWithException


@OptIn(ExperimentalStdlibApi::class)
fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, action: suspend () -> Unit): Job {
public fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, action: suspend () -> Unit): Job {
val state = RawReactive<Unit>()
val result = launch(
context,
Expand All @@ -35,8 +35,8 @@ fun CoroutineScope.load(context: CoroutineContext = EmptyCoroutineContext, actio
return result
}

class WaitGate(permit: Boolean = false) {
var permit: Boolean = permit
public class WaitGate(permit: Boolean = false) {
public var permit: Boolean = permit
set(value) {
field = value
if (value) {
Expand All @@ -46,18 +46,18 @@ class WaitGate(permit: Boolean = false) {
continuations.clear()
}
}
fun permitOnce() {
public fun permitOnce() {
permit = true
permit = false
}
val continuations = ArrayList<Continuation<Unit>>()
suspend fun await(): Unit {
private val continuations = ArrayList<Continuation<Unit>>()
public suspend fun await(): Unit {
if (permit) return
else return suspendCancellableCoroutine {
continuations.add(it)
}
}
fun abandon() {
public fun abandon() {
for (continuation in continuations) {
continuation.resumeWithException(CancellationException("abandoned as requested"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import com.lightningkite.reactive.core.addAndRunListener
import kotlinx.coroutines.*
import kotlin.coroutines.*

abstract class DependencyChangeListener : DependencyTracker(), CoroutineContext.Element {
public abstract class DependencyChangeListener : DependencyTracker(), CoroutineContext.Element {
override val key: CoroutineContext.Key<DependencyChangeListener> get() = Key

object Key : CoroutineContext.Key<DependencyChangeListener>
public object Key : CoroutineContext.Key<DependencyChangeListener>

abstract fun onDependencyChange()
open fun onDependencyNotReady() = onDependencyChange()
public abstract fun onDependencyChange()
public open fun onDependencyNotReady(): Unit = onDependencyChange()
}

private fun <T> Continuation<T>.resumeState(state: ReactiveState<T>) {
Expand All @@ -25,19 +25,19 @@ private fun <T> Continuation<T>.resumeState(state: ReactiveState<T>) {
)
}

suspend fun rerunOn(listenable: Listenable) {
public suspend fun rerunOn(listenable: Listenable) {
currentCoroutineContext()[DependencyChangeListener.Key]?.let {
if (it.existingDependency(listenable) == null) {
it.registerDependency(listenable, listenable.addListener { it.onDependencyChange() })
}
}
}

suspend inline operator fun <T> Reactive<T>.invoke(): T = await()
suspend inline operator fun <T> ReactiveValue<T>.invoke(): T = await()
suspend inline fun <T> Reactive<T>.exception(): Exception? = state { it.exception }
public suspend inline operator fun <T> Reactive<T>.invoke(): T = await()
public suspend inline operator fun <T> ReactiveValue<T>.invoke(): T = await()
public suspend inline fun <T> Reactive<T>.exception(): Exception? = state { it.exception }

suspend fun <T, V> Reactive<T>.state(get: (ReactiveState<T>) -> V): V {
public suspend fun <T, V> Reactive<T>.state(get: (ReactiveState<T>) -> V): V {
return currentCoroutineContext()[DependencyChangeListener.Key]?.let {
// and the value is ready to go, just add the listener and proceed with the value.
var last = state.let(get)
Expand All @@ -56,7 +56,7 @@ suspend fun <T, V> Reactive<T>.state(get: (ReactiveState<T>) -> V): V {
} ?: state.let(get)
}

suspend fun <T> Reactive<T>.state(): ReactiveState<T> {
public suspend fun <T> Reactive<T>.state(): ReactiveState<T> {
return currentCoroutineContext()[DependencyChangeListener.Key]?.let {
// and the value is ready to go, just add the listener and proceed with the value.
var last = state
Expand All @@ -75,7 +75,7 @@ suspend fun <T> Reactive<T>.state(): ReactiveState<T> {
} ?: state
}

suspend fun <T> ReactiveValue<T>.await(): T {
public suspend fun <T> ReactiveValue<T>.await(): T {
return currentCoroutineContext()[DependencyChangeListener.Key]?.let {
// and the value is ready to go, just add the listener and proceed with the value.
var last = value
Expand All @@ -94,7 +94,7 @@ suspend fun <T> ReactiveValue<T>.await(): T {
} ?: value
}

suspend fun <T> Reactive<T>.await(): T {
public suspend fun <T> Reactive<T>.await(): T {
return currentCoroutineContext()[DependencyChangeListener.Key]?.let {
var cont: Continuation<T>? = null
if (it.existingDependency(this) == null) {
Expand Down Expand Up @@ -138,7 +138,7 @@ suspend fun <T> Reactive<T>.await(): T {
* source's own nature, as with a `Signal` - is keeping that value current. Only [ReactiveState.notActive]
* and notReady require listening, and the subscription is released as soon as a value arrives.
*/
suspend fun <T> Reactive<T>.awaitOnce(): T {
public suspend fun <T> Reactive<T>.awaitOnce(): T {
val state = state
@Suppress("DEPRECATION")
return if (state.ready) state.get()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.lightningkite.reactive.context

abstract class DependencyTracker {
public abstract class DependencyTracker {
private val dependencies = ArrayList<Pair<Any, () -> Unit>>()
private val usedDependencies = ArrayList<Any>()

Expand All @@ -19,7 +19,7 @@ abstract class DependencyTracker {
* so the fast-path alignment above holds even when a call site re-reads an earlier dependency.
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> existingDependency(listenable: T): T? {
public fun <T : Any> existingDependency(listenable: T): T? {
val index = usedDependencies.size
if (index < dependencies.size) {
val maybe = dependencies[index].first
Expand All @@ -33,11 +33,11 @@ abstract class DependencyTracker {
return found
}

fun registerDependency(any: Any, remove: () -> Unit) {
public fun registerDependency(any: Any, remove: () -> Unit) {
this.dependencies += any to remove
}

open fun cancel() {
public open fun cancel() {
dependencies.forEach { it.second() }
dependencies.clear()
}
Expand Down
Loading
Loading