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

nightcrawler.concurrent.HashExecutorService.kt Maven / Gradle / Ivy

The newest version!
package nightcrawler.concurrent

import nightcrawler.concurrent.exception.HashExecutorServiceException
import nightcrawler.concurrent.exception.HashExecutorServiceInterruptedException
import java.util.ArrayList
import java.util.concurrent.Callable
import java.util.concurrent.CancellationException
import java.util.concurrent.ExecutionException
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.FutureTask
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import java.util.function.Function


class HashExecutorService(
    pool: Collection,
    private val bucketMapper: Function
) : ExecutorService {
    companion object {
        @JvmStatic
        fun of(poolSize: Int, numThreadsPerPool: Int): HashExecutorService {
            val pool = (0 until poolSize).map {
                Executors.newFixedThreadPool(numThreadsPerPool)
            }
            return HashExecutorService(pool) {
                it.hashCode() % poolSize
            }
        }

        @JvmStatic
        fun singleThreaded(poolSize: Int): HashExecutorService {
            return of(poolSize, numThreadsPerPool = 1)
        }
    }

    private val pool = pool.toTypedArray()

    override fun execute(command: Runnable) {
        pool[command.index].execute(command)
    }

    override fun shutdown() {
        doSafe { shutdown() }
    }

    override fun shutdownNow(): List {
        return doSafe { shutdownNow() }.flatten()
    }

    override fun isShutdown(): Boolean {
        return doSafe { isShutdown }.all { it }
    }

    override fun isTerminated(): Boolean {
        return doSafe { isTerminated }.all { it }
    }

    @Throws(InterruptedException::class)
    override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
        return Executors.newFixedThreadPool(pool.size)
            .invokeAll(pool.map { Callable { it.awaitTermination(timeout, unit) } })
            .map { it.get() }
            .all { it }
    }

    override fun  submit(task: Callable): Future {
        return pool[task.index].submit(task)
    }

    override fun  submit(task: Runnable, result: T): Future {
        return pool[task.index].submit(task, result)
    }

    override fun submit(task: Runnable): Future<*> {
        return pool[task.index].submit(task)
    }

    @Throws(InterruptedException::class)
    override fun  invokeAll(tasks: Collection>): List> {
        val futures = tasks.map { FutureTask(it) }

        try {
            tasks.forEachIndexed { index, callable ->
                val future = futures[index]
                pool[callable.index].execute(future)
            }
            futures.blockWait()

            return futures
        } catch (ex: Throwable) {
            futures.cancelAll()
            throw ex
        }
    }

    @Throws(InterruptedException::class)
    override fun  invokeAll(
        tasks: Collection>,
        timeout: Long,
        unit: TimeUnit
    ): List> {
        val nanos = unit.toNanos(timeout)
        val deadline = System.nanoTime() + nanos
        val futures = tasks.map { FutureTask(it) }
        try {
            tasks.forEachIndexed { index, callable ->
                val future = futures[index]
                if (deadline < System.nanoTime()) {
                    futures.cancelAll()
                    return futures
                }
                pool[callable.index].execute(future)
            }

            futures.forEachIndexed() { index, future ->
                if (!future.isDone) {
                    try {
                        future.get(deadline - System.nanoTime(), TimeUnit.NANOSECONDS)
                    } catch (ignore: CancellationException) {
                    } catch (ignore: ExecutionException) {
                    } catch (ex: TimeoutException) {
                        futures.cancelAll(index)
                        return futures
                    }
                }
            }

            return futures
        } catch (ex: Throwable) {
            futures.cancelAll()
            throw ex
        }
    }

    @Throws(InterruptedException::class, ExecutionException::class)
    override fun  invokeAny(tasks: Collection>): T {
        return doInvokeAny(tasks, false, 0)
    }

    @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
    override fun  invokeAny(tasks: Collection>, timeout: Long, unit: TimeUnit): T {
        return doInvokeAny(tasks, true, unit.toNanos(timeout))
    }

    private fun  doSafe(block: ExecutorService.() -> T): List {
        val errors = mutableListOf()
        var hasInterruptedException = false
        val results = pool.mapNotNull { executorService ->
            try {
                block.invoke(executorService)
            } catch (ex: Throwable) {
                if (ex is InterruptedException) {
                    hasInterruptedException = true
                }
                errors.add(ex)
                null
            }
        }

        if (errors.isNotEmpty()) {
            if (hasInterruptedException) {
                throw HashExecutorServiceInterruptedException(errors)
            }
            throw HashExecutorServiceException(errors)
        }
        return results
    }

    @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
    private fun  doInvokeAny(
        tasks: Collection>,
        timed: Boolean,
        timeout: Long
    ): T {
        var nanos = timeout
        var ntasks = tasks.size
        require(ntasks != 0)
        val futures = ArrayList>(ntasks)
        val ecs = HashExecutorCompletionService(this)

        // For efficiency, especially in executors with limited
        // parallelism, check to see if previously submitted tasks are
        // done before submitting more of them. This interleaving
        // plus the exception mechanics account for messiness of main
        // loop.
        try {
            // Record exceptions so that if we fail to obtain any
            // result, we can throw the last exception we got.
            var ee: ExecutionException? = null
            val deadline = if (timed) System.nanoTime() + nanos else 0L
            val it = tasks.iterator()

            // Start one task for sure; the rest incrementally
            futures.add(ecs.submit(it.next()))
            --ntasks
            var active = 1
            while (true) {
                var f = ecs.poll()
                if (f == null) {
                    if (ntasks > 0) {
                        --ntasks
                        futures.add(ecs.submit(it.next()))
                        ++active
                    } else if (active == 0) {
                        break
                    } else if (timed) {
                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS)
                        if (f == null) throw TimeoutException()
                        nanos = deadline - System.nanoTime()
                    } else {
                        f = ecs.take()
                    }
                }
                if (f != null) {
                    --active
                    ee = try {
                        return f.get()
                    } catch (eex: ExecutionException) {
                        eex
                    } catch (rex: RuntimeException) {
                        ExecutionException(rex)
                    }
                }
            }
            if (ee == null) {
                ee = ExecutionException(null)
            }
            throw ee
        } finally {
            futures.cancelAll()
        }
    }

    private val Runnable.index get() = bucketMapper.apply(this)

    private val Callable<*>.index get() = bucketMapper.apply(this)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy