All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.pubnub.kmp.futures.kt Maven / Gradle / Ivy

Go to download

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!

There is a newer version: 10.2.0
Show newest version
@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)
}