commonMain.com.pubnub.kmp.futures.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pubnub-kotlin-api Show documentation
Show all versions of pubnub-kotlin-api Show documentation
PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter second!
@file:JvmName("PNFutures")
package com.pubnub.kmp
import com.pubnub.api.PubNubException
import com.pubnub.api.v2.callbacks.Consumer
import com.pubnub.api.v2.callbacks.Result
import com.pubnub.api.v2.callbacks.mapCatching
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.locks.ReentrantLock
import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.withLock
import kotlin.jvm.JvmName
private class CompletablePNFuture : PNFuture {
private val reentrantLock: ReentrantLock = reentrantLock()
private lateinit var result: Result
private var callback: Consumer>? = null
fun complete(result: Result) {
reentrantLock.withLock {
if (!this::result.isInitialized) {
this.result = result
callback?.accept(result)
} else {
error("This CompletablePNFuture has already completed with result $result")
}
}
}
override fun async(callback: Consumer>) {
reentrantLock.withLock {
if (this.callback != null) {
error("Only one callback is supported for CompletablePNFuture.")
}
if (!this::result.isInitialized) {
this.callback = callback
} else {
callback.accept(result)
}
}
}
}
fun PubNubException.asFuture(): PNFuture = PNFuture { callback ->
callback.accept(Result.failure(this@asFuture))
}
fun T.asFuture(): PNFuture = PNFuture { callback ->
callback.accept(Result.success(this@asFuture))
}
fun Result.asFuture(): PNFuture = PNFuture { callback ->
callback.accept(this@asFuture)
}
fun PNFuture.then(action: (T) -> U): PNFuture = PNFuture { callback ->
[email protected] { it: Result ->
callback.accept(it.mapCatching(action))
}
}
fun PNFuture.thenAsync(action: (T) -> PNFuture): PNFuture = PNFuture { callback ->
[email protected] { firstFutureResult: Result ->
val intermediateResult: Result> = firstFutureResult.mapCatching(action)
intermediateResult.onFailure {
callback.accept(Result.failure(it))
}.onSuccess { secondFuture: PNFuture ->
secondFuture.async(callback)
}
}
}
fun PNFuture.remember(): PNFuture = CompletablePNFuture().also { completableFuture ->
[email protected] {
completableFuture.complete(it)
}
}
/**
* Execute a second PNFuture after this PNFuture completes successfully,
* and return the *original* value of this PNFuture after the second PNFuture completes successfully.
*
* Failures are propagated to the resulting PNFuture.
*/
fun PNFuture.alsoAsync(action: (T) -> PNFuture<*>): PNFuture =
[email protected] { outerResult: T ->
action(outerResult).then { _ ->
outerResult
}
}
fun PNFuture.catch(action: (Exception) -> Result): PNFuture = PNFuture { callback ->
[email protected] { result: Result ->
result.onSuccess {
callback.accept(result)
}.onFailure {
try {
callback.accept(action(it))
} catch (e: Exception) {
callback.accept(Result.failure(e))
}
}
}
}
fun Collection>.awaitAll(): PNFuture> = PNFuture { callback ->
if (isEmpty()) {
callback.accept(Result.success(emptyList()))
return@PNFuture
}
val counter = atomic(0)
val failed = atomic(false)
val array = Array(size) { null }
forEachIndexed { index, future ->
future.async { res ->
res.onSuccess { value ->
array[index] = value
val counterIncremented = counter.incrementAndGet()
if (counterIncremented == size) {
callback.accept(Result.success(array.toList() as List))
}
}.onFailure { exception ->
val failedWasSetPreviously = failed.getAndSet(true)
if (!failedWasSetPreviously) {
callback.accept(Result.failure(exception))
}
}
}
}
}
@Suppress("UNCHECKED_CAST")
fun awaitAll(
future1: PNFuture,
future2: PNFuture
): PNFuture> = listOf(future1 as PNFuture, future2 as PNFuture).awaitAll().then { it: List ->
Pair(it[0] as T, it[1] as U)
}
@Suppress("UNCHECKED_CAST")
fun awaitAll(
future1: PNFuture,
future2: PNFuture,
future3: PNFuture
): PNFuture> = listOf(future1 as PNFuture, future2 as PNFuture, future3 as PNFuture).awaitAll().then {
it: List ->
Triple(it[0] as T, it[1] as U, it[2] as X)
}