mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-06 08:38:30 +09:00
trying to fix the threadexecutor, at least it will throw error if a job failed
This commit is contained in:
@@ -1,9 +1,6 @@
|
|||||||
package net.torvald.terrarum.concurrent
|
package net.torvald.terrarum.concurrent
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.*
|
||||||
import java.util.concurrent.Executors
|
|
||||||
import java.util.concurrent.Future
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
typealias RunnableFun = () -> Unit
|
typealias RunnableFun = () -> Unit
|
||||||
@@ -14,32 +11,58 @@ typealias ThreadableFun = (Int) -> Unit
|
|||||||
object ThreadExecutor {
|
object ThreadExecutor {
|
||||||
val threadCount = Runtime.getRuntime().availableProcessors() // not using (logicalCores + 1) method; it's often better idea to reserve one extra thread for other jobs in the app
|
val threadCount = Runtime.getRuntime().availableProcessors() // not using (logicalCores + 1) method; it's often better idea to reserve one extra thread for other jobs in the app
|
||||||
private lateinit var executor: ExecutorService// = Executors.newFixedThreadPool(threadCount)
|
private lateinit var executor: ExecutorService// = Executors.newFixedThreadPool(threadCount)
|
||||||
|
val futures = ArrayList<Future<*>>()
|
||||||
|
private var isOpen = true
|
||||||
|
|
||||||
private fun checkShutdown() {
|
private fun checkShutdown() {
|
||||||
try {
|
try {
|
||||||
if (executor.isTerminated)
|
if (executor.isTerminated)
|
||||||
throw IllegalStateException("Executor terminated, renew the executor service.")
|
throw IllegalStateException("Executor terminated, renew the executor service.")
|
||||||
if (executor.isShutdown)
|
if (!isOpen || executor.isShutdown)
|
||||||
throw IllegalStateException("Pool is closed, come back when all the threads are terminated.")
|
throw IllegalStateException("Pool is closed, come back when all the threads are terminated.")
|
||||||
}
|
}
|
||||||
catch (e: UninitializedPropertyAccessException) {}
|
catch (e: UninitializedPropertyAccessException) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun renew() {
|
fun renew() {
|
||||||
|
try {
|
||||||
|
if (!executor.isTerminated && !executor.isShutdown) throw IllegalStateException("Pool is still running")
|
||||||
|
}
|
||||||
|
catch (_: UninitializedPropertyAccessException) {}
|
||||||
|
|
||||||
executor = Executors.newFixedThreadPool(threadCount)
|
executor = Executors.newFixedThreadPool(threadCount)
|
||||||
|
futures.clear()
|
||||||
|
isOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun submit(t: Runnable): Future<*> {
|
/*fun invokeAll(ts: List<Callable<Unit>>) {
|
||||||
checkShutdown()
|
checkShutdown()
|
||||||
return executor.submit(t)
|
executor.invokeAll(ts)
|
||||||
}
|
}*/
|
||||||
fun submit(f: RunnableFun): Future<*> {
|
|
||||||
checkShutdown()
|
|
||||||
return executor.submit { f() }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fun submit(t: Callable<Unit>) {
|
||||||
|
checkShutdown()
|
||||||
|
val fut = executor.submit(t)
|
||||||
|
futures.add(fut)
|
||||||
|
}
|
||||||
|
/*fun submit(f: RunnableFun) {
|
||||||
|
checkShutdown()
|
||||||
|
val fut = executor.submit { f() }
|
||||||
|
futures.add(fut)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/28818494/threads-stopping-prematurely-for-certain-values
|
||||||
fun join() {
|
fun join() {
|
||||||
println("ThreadExecutor.join")
|
println("ThreadExecutor.join")
|
||||||
|
isOpen = false
|
||||||
|
futures.forEach {
|
||||||
|
try {
|
||||||
|
it.get()
|
||||||
|
}
|
||||||
|
catch (e: ExecutionException) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
executor.shutdown() // thread status of completed ones will be WAIT instead of TERMINATED without this line...
|
executor.shutdown() // thread status of completed ones will be WAIT instead of TERMINATED without this line...
|
||||||
executor.awaitTermination(24L, TimeUnit.HOURS)
|
executor.awaitTermination(24L, TimeUnit.HOURS)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,21 +23,13 @@ class Biomegen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
|||||||
|
|
||||||
private val genSlices = maxOf(ThreadExecutor.threadCount, world.width / 8)
|
private val genSlices = maxOf(ThreadExecutor.threadCount, world.width / 8)
|
||||||
|
|
||||||
private var genFutures: Array<Future<*>?> = arrayOfNulls(genSlices)
|
|
||||||
override var generationStarted: Boolean = false
|
|
||||||
override val generationDone: Boolean
|
|
||||||
get() = generationStarted && genFutures.fold(true) { acc, f -> acc and (f?.isDone ?: true) }
|
|
||||||
|
|
||||||
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
||||||
private val YHEIGHT_DIVISOR = 2.0 / 7.0
|
private val YHEIGHT_DIVISOR = 2.0 / 7.0
|
||||||
|
|
||||||
override fun run() {
|
override fun getDone() {
|
||||||
|
|
||||||
generationStarted = true
|
|
||||||
|
|
||||||
ThreadExecutor.renew()
|
ThreadExecutor.renew()
|
||||||
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
|
(0 until world.width).sliceEvenly(genSlices).map { xs ->
|
||||||
genFutures[i] = ThreadExecutor.submit {
|
ThreadExecutor.submit {
|
||||||
val localJoise = getGenerator(seed, params as BiomegenParams)
|
val localJoise = getGenerator(seed, params as BiomegenParams)
|
||||||
for (x in xs) {
|
for (x in xs) {
|
||||||
for (y in 0 until world.height) {
|
for (y in 0 until world.height) {
|
||||||
|
|||||||
@@ -20,33 +20,28 @@ class Terragen(world: GameWorld, seed: Long, params: Any) : Gen(world, seed, par
|
|||||||
|
|
||||||
private val genSlices = maxOf(ThreadExecutor.threadCount, world.width / 8)
|
private val genSlices = maxOf(ThreadExecutor.threadCount, world.width / 8)
|
||||||
|
|
||||||
private var genFutures: Array<Future<*>?> = arrayOfNulls(genSlices)
|
|
||||||
override var generationStarted: Boolean = false
|
|
||||||
override val generationDone: Boolean
|
|
||||||
get() = generationStarted && genFutures.fold(true) { acc, f -> acc and (f?.isDone ?: true) }
|
|
||||||
|
|
||||||
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
private val YHEIGHT_MAGIC = 2800.0 / 3.0
|
||||||
private val YHEIGHT_DIVISOR = 2.0 / 7.0
|
private val YHEIGHT_DIVISOR = 2.0 / 7.0
|
||||||
|
|
||||||
override fun run() {
|
override fun getDone() {
|
||||||
|
|
||||||
generationStarted = true
|
|
||||||
|
|
||||||
ThreadExecutor.renew()
|
ThreadExecutor.renew()
|
||||||
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
|
(0 until world.width).sliceEvenly(genSlices).mapIndexed { i, xs ->
|
||||||
genFutures[i] = ThreadExecutor.submit {
|
ThreadExecutor.submit {
|
||||||
val localJoise = getGenerator(seed, params as TerragenParams)
|
val localJoise = getGenerator(seed, params as TerragenParams)
|
||||||
for (x in xs) {
|
val localLock = java.lang.Object() // in an attempt to fix the "premature exit" issue of a thread run
|
||||||
for (y in 0 until world.height) {
|
synchronized(localLock) { // also see: https://stackoverflow.com/questions/28818494/threads-stopping-prematurely-for-certain-values
|
||||||
val sampleTheta = (x.toDouble() / world.width) * TWO_PI
|
for (x in xs) {
|
||||||
val sampleOffset = world.width / 8.0
|
for (y in 0 until world.height) {
|
||||||
val sampleX = sin(sampleTheta) * sampleOffset + sampleOffset // plus sampleOffset to make only
|
val sampleTheta = (x.toDouble() / world.width) * TWO_PI
|
||||||
val sampleZ = cos(sampleTheta) * sampleOffset + sampleOffset // positive points are to be sampled
|
val sampleOffset = world.width / 8.0
|
||||||
val sampleY = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant
|
val sampleX = sin(sampleTheta) * sampleOffset + sampleOffset // plus sampleOffset to make only
|
||||||
// DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant
|
val sampleZ = cos(sampleTheta) * sampleOffset + sampleOffset // positive points are to be sampled
|
||||||
val noise = localJoise.map { it.get(sampleX, sampleY, sampleZ) }
|
val sampleY = y - (world.height - YHEIGHT_MAGIC) * YHEIGHT_DIVISOR // Q&D offsetting to make ratio of sky:ground to be constant
|
||||||
|
// DEBUG NOTE: it is the OFFSET FROM THE IDEAL VALUE (observed land height - (HEIGHT * DIVISOR)) that must be constant
|
||||||
|
val noise = localJoise.map { it.get(sampleX, sampleY, sampleZ) }
|
||||||
|
|
||||||
draw(x, y, noise, world)
|
draw(x, y, noise, world)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.torvald.terrarum.modulebasegame.worldgenerator
|
|||||||
import net.torvald.terrarum.AppLoader
|
import net.torvald.terrarum.AppLoader
|
||||||
import net.torvald.terrarum.AppLoader.printdbg
|
import net.torvald.terrarum.AppLoader.printdbg
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New world generator.
|
* New world generator.
|
||||||
@@ -34,7 +35,7 @@ object Worldgen {
|
|||||||
val it = jobs[i]
|
val it = jobs[i]
|
||||||
|
|
||||||
AppLoader.getLoadScreen().addMessage(it.loadingScreenName)
|
AppLoader.getLoadScreen().addMessage(it.loadingScreenName)
|
||||||
it.theWork.run()
|
it.theWork.getDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
printdbg(this, "Generation job finished")
|
printdbg(this, "Generation job finished")
|
||||||
@@ -46,9 +47,7 @@ object Worldgen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any) {
|
abstract class Gen(val world: GameWorld, val seed: Long, val params: Any) {
|
||||||
abstract var generationStarted: Boolean
|
open fun getDone() { } // trying to use different name so that it won't be confused with Runnable or Callable
|
||||||
abstract val generationDone: Boolean
|
|
||||||
open fun run() { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class WorldgenParams(
|
data class WorldgenParams(
|
||||||
|
|||||||
Reference in New Issue
Block a user