-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement ObservableValue<T>.asFlow()
- Loading branch information
1 parent
6810745
commit 3f795b1
Showing
3 changed files
with
54 additions
and
1 deletion.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
binary-compatibility-validator/reference-public-api/kotlinx-coroutines-javafx.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package kotlinx.coroutines.javafx | ||
|
||
import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
import javafx.beans.value.ChangeListener | ||
import javafx.beans.value.ObservableValue | ||
import kotlinx.coroutines.channels.awaitClose | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.callbackFlow | ||
import kotlinx.coroutines.flow.conflate | ||
import java.util.concurrent.atomic.AtomicInteger | ||
|
||
/** | ||
* Creates an instance of a cold [Flow] that subscribes to the given [ObservableValue] and produces | ||
* its values as they change. | ||
* | ||
* The resulting flow is conflated, meaning that if several values arrive in a quick succession, only | ||
* the last one will be produced. | ||
* | ||
* It produces at least one value. | ||
* | ||
* Since this implementation uses [ObservableValue.addListener], even if this [ObservableValue] | ||
* supports lazy evaluation, eager computation will be enforced while the flow is being collected. | ||
*/ | ||
@ExperimentalCoroutinesApi | ||
fun <T: Any> ObservableValue<T>.asFlow(): Flow<T> = callbackFlow<T> { | ||
// It is unknown which thread will produce the initial value | ||
val UNKNOWN_SOURCE = 0 | ||
// 1 -- some thread succeeded in CAS, so it will offer the first value | ||
val DETERMINED_SOURCE = 1 | ||
// 2 -- the first value has been offered, so everyone else may proceed | ||
val INITIAL_OFFER_PASSED = 2 | ||
val initialOfferState = AtomicInteger(UNKNOWN_SOURCE) | ||
val listener = ChangeListener<T> { observable, oldValue, newValue -> | ||
while (initialOfferState.get() != INITIAL_OFFER_PASSED) { | ||
if (initialOfferState.compareAndSet(UNKNOWN_SOURCE, DETERMINED_SOURCE)) { | ||
offer(newValue) | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
dkhalanskyjb
Author
Collaborator
|
||
initialOfferState.set(INITIAL_OFFER_PASSED) | ||
return@ChangeListener | ||
} | ||
} | ||
offer(newValue) | ||
} | ||
addListener(listener) | ||
if (initialOfferState.compareAndSet(UNKNOWN_SOURCE, DETERMINED_SOURCE)) { | ||
send(value) | ||
initialOfferState.set(INITIAL_OFFER_PASSED) | ||
} | ||
awaitClose { removeListener(listener) } | ||
}.conflate() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This will crash the program when the flow is closed as the listener has not been unregistered yet as awaitClose didn't execute its lambda yet and a new value is arriving in the callback. See #974