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

.kovenant.kovenant-core.3.3.0.source-code.promises-jvm.kt Maven / Gradle / Ivy

/*
 * Copyright (c) 2015 Mark Platvoet
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * THE SOFTWARE.
 */

package nl.komponents.kovenant

import nl.komponents.kovenant.unsafe.UnsafeAtomicReferenceFieldUpdater
import nl.komponents.kovenant.unsafe.hasUnsafe
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater

internal fun  concretePromise(context: Context, callable: () -> V): Promise
        = TaskPromise(context, callable)

internal fun  concretePromise(context: Context, promise: Promise, callable: (V) -> R): Promise
        = ThenPromise(context, promise, callable)

internal fun  concreteSuccessfulPromise(context: Context, value: V): Promise = SuccessfulPromise(context, value)

internal fun  concreteFailedPromise(context: Context, value: E): Promise = FailedPromise(context, value)

internal fun  concreteDeferred(context: Context): Deferred = DeferredPromise(context)

internal fun  concreteDeferred(context: Context, onCancelled: (E) -> Unit): Deferred = CancelableDeferredPromise(context, onCancelled)

private class SuccessfulPromise(context: Context, value: V) : AbstractPromise(context) {
    init {
        trySetSuccessResult(value)
    }

    // Could override the `fail` methods since there is nothing to add
    // but any change to those methods might be missed.
    // the callbacks essentially get ignored anyway
}

private class FailedPromise(context: Context, value: E) : AbstractPromise(context) {
    init {
        trySetFailResult(value)
    }

    // Could override the `success` methods since there is nothing to add
    // but any change to those methods might be missed.
    // the callbacks essentially get ignored anyway
}

private class ThenPromise(context: Context,
                                promise: Promise,
                                callable: (V) -> R) :
        SelfResolvingPromise(context),
        CancelablePromise {


    //need to hold the task to be able to cancel
    private @Volatile var task: (() -> Unit)? = null

    init {
        if (promise.isDone()) {
            if (promise.isSuccess()) {
                schedule(context, promise.get(), callable)
            } else {
                reject(promise.getError())
            }
        } else {
            triggered(callable, context, promise)
        }
    }

    private fun triggered(callable: (V) -> R, context: Context, promise: Promise) {
        promise.success(DirectDispatcherContext) {
            schedule(context, it, callable)
        }

        promise.fail(DirectDispatcherContext) {
            reject(it)
        }
    }

    private fun schedule(context: Context, value: V, callable: (V) -> R) {
        val wrapper = {
            try {
                val result = callable(value)
                resolve(result)
            } catch(e: Exception) {
                reject(e)
            } finally {
                //avoid leaking memory after a reject/resolve
                task = null
            }
        }
        task = wrapper
        context.workerContext.offer(wrapper)
    }


    override fun cancel(error: Exception): Boolean {
        val wrapper = task
        if (wrapper != null) {
            task = null //avoid memory leaking
            context.workerContext.dispatcher.tryCancel(wrapper)


            if (trySetFailResult(error)) {
                fireFail(error)
                return true
            }
        }

        return false
    }

}

private class TaskPromise(context: Context, callable: () -> V) :
        SelfResolvingPromise(context),
        CancelablePromise {
    private @Volatile var task: (() -> Unit)?

    init {
        val wrapper = {
            try {
                val result = callable()
                resolve(result)
            } catch(e: Exception) {
                reject(e)
            } finally {
                //avoid leaking memory after a reject/resolve
                task = null
            }
        }
        task = wrapper
        context.workerContext.offer(wrapper)
    }

    override fun cancel(error: Exception): Boolean {
        val wrapper = task
        if (wrapper != null) {
            task = null //avoid memory leaking
            context.workerContext.dispatcher.tryCancel(wrapper)


            if (trySetFailResult(error)) {
                fireFail(error)
                return true
            }
        }

        return false
    }
}

private abstract class SelfResolvingPromise(context: Context) : AbstractPromise(context) {
    protected fun resolve(value: V) {
        if (trySetSuccessResult(value)) {
            fireSuccess(value)
        }
        //no need to report multiple completion here.
        //manage this ourselves, can't happen
    }

    protected fun reject(error: E) {
        if (trySetFailResult(error)) {
            fireFail(error)
        }
        //no need to report multiple completion here.
        //manage this ourselves, can't happen
    }
}

private class DeferredPromise(context: Context) : AbstractPromise(context), Deferred {
    override fun resolve(value: V) {
        if (trySetSuccessResult(value)) {
            fireSuccess(value)
        } else {
            multipleCompletion(value)
        }
    }

    override fun reject(error: E) {
        if (trySetFailResult(error)) {
            fireFail(error)
        } else {
            multipleCompletion(error)
        }
    }

    //Only call this method if we know resolving is eminent.
    private fun multipleCompletion(newValue: Any?) {
        while (!isDoneInternal()) {
            Thread.`yield`()
        }
        context.multipleCompletion(rawValue(), newValue)
    }

    override val promise: Promise = object : Promise by this {}
}

private class CancelableDeferredPromise(context: Context, onCancelled: (E) -> Unit) :
        AbstractPromise(context), Deferred,
        CancelablePromise {

    companion object {
        //prefer ints over enums for class count
        //matters on Android
        private val unresolved = 0
        private val cancelled = 1
        private val failed = 2
        private val success = 3
    }

    //allow callback to be cleaned. Promise can live a long time so avoid
    //callback holding to resources not important anymore
    private @Volatile var cb: ((E) -> Unit)? = onCancelled

    private @Volatile var state = unresolved

    override fun resolve(value: V) {
        while (state == unresolved) {
            if (trySetSuccessResult(value)) {
                state = success
                fireSuccess(value)
                cb = null
                return
            }
        }

        //Only report multiple completion if this has not been cancelled
        //since in race situations this can simply happen.
        if (state != cancelled) {
            multipleCompletion(value)
        }
    }

    override fun reject(error: E) {
        while (state == unresolved) {
            if (trySetFailResult(error)) {
                state = failed
                fireFail(error)
                cb = null
                return
            }
        }

        //Only report multiple completion if this has not been cancelled
        //since in race situations this can simply happen.
        if (state != cancelled) {
            multipleCompletion(error)
        }
    }

    override fun cancel(error: E): Boolean {
        while (state == unresolved) {
            if (trySetFailResult(error)) {
                state = cancelled

                //Try to actually cancel before firing the callbacks.
                //this to avoid situations where someone expects an happens-before

                val exc = tryCancelCb(error)

                fireFail(error)
                cb = null

                return when (exc) {
                    null -> true
                    else -> throw KovenantException("Promise cancelled but cancel might not have succeeded due to exception in callback", exc)
                }
            }
        }
        return false
    }

    private fun tryCancelCb(error: E): Exception? {
        try {
            cb?.invoke(error)
        } catch (e: Exception) {
            return e
        }
        return null
    }

    //Only call this method if we know resolving is eminent.
    private fun multipleCompletion(newValue: Any?) {
        while (!isDoneInternal()) {
            Thread.`yield`()
        }
        context.multipleCompletion(rawValue(), newValue)
    }

    override val promise: Promise = object : CancelablePromise by this {}
}

private abstract class AbstractPromise(override val context: Context) : Promise {

    companion object {
        private val stateUpdater: AtomicReferenceFieldUpdater, State>
        private val waitingThreadsUpdater: AtomicReferenceFieldUpdater, AtomicInteger>
        private val headUpdater: AtomicReferenceFieldUpdater, CallbackContextNode<*, *>>

        init {
            if (hasUnsafe()) {
                stateUpdater = UnsafeAtomicReferenceFieldUpdater(AbstractPromise::class, "state")
                waitingThreadsUpdater = UnsafeAtomicReferenceFieldUpdater(AbstractPromise::class, "_waitingThreads")
                headUpdater = UnsafeAtomicReferenceFieldUpdater(AbstractPromise::class, "_head")
            } else {
                stateUpdater = AtomicReferenceFieldUpdater.newUpdater(AbstractPromise::class.java, State::class.java, "state")
                waitingThreadsUpdater = AtomicReferenceFieldUpdater.newUpdater(AbstractPromise::class.java, AtomicInteger::class.java, "_waitingThreads")
                headUpdater = AtomicReferenceFieldUpdater.newUpdater(AbstractPromise::class.java, CallbackContextNode::class.java, "_head")
            }
        }
    }

    private @Volatile var state = State.PENDING
    private @Volatile var _waitingThreads: AtomicInteger? = null

    private val waitingThreads: AtomicInteger get() {
        while (true) {
            val m = _waitingThreads
            if (m != null) {
                return m
            }
            waitingThreadsUpdater.compareAndSet(this, null, AtomicInteger(0))
        }
    }

    @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
    private val mutex: Object get() = waitingThreads as Object

    private @Volatile var _head: CallbackContextNode? = null


    private @Volatile var result: Any? = null


    override fun success(context: DispatcherContext, callback: (value: V) -> Unit): Promise {
        if (isFailureInternal()) return this

        //Bypass the queue if this promise is resolved and the queue is empty
        //no need to create excess nodes
        if (isSuccessInternal() && isEmptyCallbacks()) {
            context.offer { callback(getAsValueResult()) }
            return this
        }

        addSuccessCb(context, callback)

        //possibly resolved already
        if (isSuccessInternal()) fireSuccess(getAsValueResult())

        return this
    }

    override fun fail(context: DispatcherContext, callback: (error: E) -> Unit): Promise {
        if (isSuccessInternal()) return this

        //Bypass the queue if this promise is resolved and the queue is empty
        //no need to create excess nodes
        if (isFailureInternal() && isEmptyCallbacks()) {
            context.offer { callback(getAsFailResult()) }
            return this
        }

        addFailCb(context, callback)

        //possibly rejected already
        if (isFailureInternal()) fireFail(getAsFailResult())

        return this
    }

    override fun always(context: DispatcherContext, callback: () -> Unit): Promise {
        //Bypass the queue if this promise is resolved and the queue is empty
        //no need to create excess nodes
        if ((isSuccessInternal() || isFailureInternal()) && isEmptyCallbacks()) {
            context.offer { callback() }
            return this
        }

        addAlwaysCb(context, callback)

        //possibly completed already
        when {
            isSuccessInternal() -> fireSuccess(getAsValueResult())
            isFailureInternal() -> fireFail((getAsFailResult()))
        }

        return this
    }

    override fun get(): V {
        if (!isDoneInternal()) {
            waitingThreads.incrementAndGet()
            try {
                synchronized(mutex) {
                    while (!isDoneInternal()) {
                        try {
                            mutex.wait()
                        } catch(e: InterruptedException) {
                            throw FailedException(e)
                        }
                    }
                }
            } finally {
                waitingThreads.decrementAndGet()
            }
        }

        if (isSuccessInternal()) {
            return getAsValueResult()
        } else {
            throw getAsFailResult().asException()
        }
    }

    override fun getError(): E {
        if (!isDoneInternal()) {
            waitingThreads.incrementAndGet()
            try {
                synchronized(mutex) {
                    while (!isDoneInternal()) {
                        try {
                            mutex.wait()
                        } catch(e: InterruptedException) {
                            throw FailedException(e)
                        }
                    }
                }
            } finally {
                waitingThreads.decrementAndGet()
            }
        }

        if (isFailureInternal()) {
            return getAsFailResult()
        } else {
            throw FailedException(getAsValueResult())
        }
    }

    fun fireSuccess(value: V) = popAll {
        node ->
        node.runSuccess(value)
    }

    fun fireFail(value: E) = popAll {
        node ->
        node.runFail(value)
    }

    private enum class State {
        PENDING,
        MUTATING,
        SUCCESS,
        FAIL
    }

    private enum class NodeState {
        CHAINED,
        POPPING,
        APPENDING
    }


    fun trySetSuccessResult(result: V): Boolean {
        if (state != State.PENDING) return false
        if (stateUpdater.compareAndSet(this, State.PENDING, State.MUTATING)) {
            this.result = result
            state = State.SUCCESS
            notifyBlockedThreads()
            return true
        }
        return false
    }

    fun trySetFailResult(result: E): Boolean {

        if (state != State.PENDING) return false
        if (stateUpdater.compareAndSet(this, State.PENDING, State.MUTATING)) {
            this.result = result
            state = State.FAIL
            notifyBlockedThreads()
            return true
        }
        return false
    }

    private fun notifyBlockedThreads() {
        val i = _waitingThreads
        if (i != null && i.get() > 0) {
            synchronized(mutex) {
                mutex.notifyAll()
            }
        }
    }


    protected fun isDoneInternal(): Boolean {
        val currentState = state
        return currentState == State.SUCCESS || currentState == State.FAIL
    }

    protected fun isSuccessInternal(): Boolean = state == State.SUCCESS
    protected fun isFailureInternal(): Boolean = state == State.FAIL

    override fun isDone(): Boolean = isDoneInternal()
    override fun isFailure(): Boolean = isFailureInternal()
    override fun isSuccess(): Boolean = isSuccessInternal()


    //For internal use only! Method doesn't check anything, just casts.
    @Suppress("UNCHECKED_CAST")
    private fun getAsValueResult(): V = result as V

    //For internal use only! Method doesn't check anything, just casts.
    @Suppress("UNCHECKED_CAST")
    private fun getAsFailResult(): E = result as E

    protected fun rawValue(): Any = result as Any

    private fun addSuccessCb(context: DispatcherContext, cb: (V) -> Unit) = addValueNode(SuccessCallbackContextNode(context, cb))

    private fun addFailCb(context: DispatcherContext, cb: (E) -> Unit) = addValueNode(FailCallbackContextNode(context, cb))

    private fun addAlwaysCb(context: DispatcherContext, cb: () -> Unit) = addValueNode(AlwaysCallbackContextNode(context, cb))

    private fun createHeadNode(): CallbackContextNode = EmptyCallbackContextNode()

    private fun addValueNode(node: CallbackContextNode) {
        while (true) {
            val tail = findTailNode()

            if (tail.compareAndSet(NodeState.CHAINED, NodeState.APPENDING)) {
                if (tail.next == null) {
                    tail.next = node
                    tail.nodeState = NodeState.CHAINED
                    return
                }
                tail.nodeState = NodeState.CHAINED
            }
        }
    }

    private fun getOrCreateHead(): CallbackContextNode {
        while (true) {
            val h = _head
            if (h != null) {
                return h
            }
            headUpdater.compareAndSet(this, null, createHeadNode())
        }
    }

    private fun findTailNode(): CallbackContextNode {
        var tail = getOrCreateHead()
        while (true) tail = tail.next ?: break
        return tail
    }

    private fun isEmptyCallbacks(): Boolean {
        val headNode = _head
        return headNode == null || headNode.next == null
    }


    private inline fun popAll(fn: (CallbackContext) -> Unit) {
        val localHead = _head

        if (localHead != null) {
            do {
                val popper = localHead.next
                if (popper != null) {
                    if (localHead.compareAndSet(NodeState.CHAINED, NodeState.POPPING)) {
                        if (popper.compareAndSet(NodeState.CHAINED, NodeState.POPPING)) {
                            localHead.next = popper.next
                            localHead.nodeState = NodeState.CHAINED
                            popper.next = null
                            fn(popper)
                        }
                        localHead.nodeState = NodeState.CHAINED
                    }
                }
            } while (popper != null)
        }
    }

    private interface CallbackContext {
        fun runSuccess(value: V)

        fun runFail(value: E)
    }

    private abstract class CallbackContextNode : CallbackContext {
        companion object {
            private val nodeStateUpdater: AtomicReferenceFieldUpdater, NodeState>

            init {
                nodeStateUpdater = if (hasUnsafe()) {
                    UnsafeAtomicReferenceFieldUpdater(CallbackContextNode::class, "nodeState")
                } else {
                    AtomicReferenceFieldUpdater.newUpdater(CallbackContextNode::class.java, NodeState::class.java, "nodeState")
                }
            }
        }

        fun compareAndSet(expected: NodeState, update: NodeState): Boolean {
            return nodeStateUpdater.compareAndSet(this, expected, update)
        }

        @Volatile var next: CallbackContextNode? = null
        @Volatile var nodeState = NodeState.CHAINED
    }

    private class EmptyCallbackContextNode : CallbackContextNode() {

        override fun runSuccess(value: V) {
            //ignore
        }

        override fun runFail(value: E) {
            //ignore
        }
    }

    private class AlwaysCallbackContextNode(private val context: DispatcherContext,
                                                  private val fn: () -> Unit) : CallbackContextNode() {
        override fun runSuccess(value: V) = context.offer { fn() }

        override fun runFail(value: E) = context.offer { fn() }
    }

    private class SuccessCallbackContextNode(private val context: DispatcherContext,
                                                   private val fn: (V) -> Unit) : CallbackContextNode() {

        override fun runSuccess(value: V) = context.offer { fn(value) }

        override fun runFail(value: E) {
            //ignore
        }
    }

    private class FailCallbackContextNode(private val context: DispatcherContext,
                                                private val fn: (E) -> Unit) : CallbackContextNode() {

        override fun runSuccess(value: V) {
            //ignore
        }

        override fun runFail(value: E) = context.offer { fn(value) }
    }

}

private fun  T.asException(): Exception {
    return when (this) {
        is Exception -> this
        else -> FailedException(this)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy