package net.corda.node.services.statemachine

import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.strands.concurrent.AbstractQueuedSynchronizer

/**
 * Quasar-compatible latch that may be incremented.
 */
class CountUpDownLatch(initialValue: Int) {

    // See quasar CountDownLatch
    private class Sync(initialValue: Int) : AbstractQueuedSynchronizer() {
        init {
            state = initialValue
        }

        override fun tryAcquireShared(arg: Int): Int {
            return if (arg >= 0) {
                if (state == arg) 1 else -1
            } else {
                if (state <= -arg) 1 else -1
            }
        }

        override fun tryReleaseShared(arg: Int): Boolean {
            while (true) {
                val c = state
                if (c == 0)
                    return false
                val nextc = c - Math.min(c, arg)
                if (compareAndSetState(c, nextc))
                    return nextc == 0
            }
        }

        fun increment() {
            while (true) {
                val c = state
                val nextc = c + 1
                if (compareAndSetState(c, nextc))
                    return
            }
        }
    }

    private val sync = Sync(initialValue)

    @Suspendable
    fun await() {
        sync.acquireSharedInterruptibly(0)
    }

    @Suspendable
    fun awaitLessThanOrEqual(number: Int) {
        sync.acquireSharedInterruptibly(number)
    }

    fun countDown(number: Int = 1) {
        require(number > 0){"Number to count down by must be greater than 0"}
        sync.releaseShared(number)
    }

    fun countUp() {
        sync.increment()
    }
}
