package ru.dbotthepony.kstarbound.util

import java.lang.ref.Reference
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.LockSupport

class CarriedExecutor(private val parent: Executor) : Executor, Runnable {
	private val queue = ConcurrentLinkedQueue<Runnable>()
	private val isCarried = AtomicBoolean()

	override fun execute(command: Runnable) {
		queue.add(command)

		if (isCarried.compareAndSet(false, true)) {
			parent.execute(this)
		}
	}

	override fun run() {
		while (true) {
			var next = queue.poll()

			while (next != null) {
				next.run()
				next = queue.poll()
			}

			isCarried.set(false)

			// spinwait for little
			var i = 10

			while (i-- > 0) {
				Reference.reachabilityFence(queue)
			}

			if (queue.isNotEmpty() && isCarried.compareAndSet(false, true)) {
				// bugger! queue updated while we have finished
				// and no other thread has picked up
			} else {
				break
			}
		}
	}

	fun wait(howLong: Long, unit: TimeUnit) {
		val deadline = System.nanoTime() + unit.toNanos(howLong)

		while (System.nanoTime() < deadline && isCarried.get()) {
			LockSupport.parkNanos(500_000L)
		}
	}
}
