Skip to content

Commit

Permalink
Use Thread.yield on the SdlLoopRunner to have a single SdlAsyncLoopRu…
Browse files Browse the repository at this point in the history
…nner
  • Loading branch information
JD557 committed Apr 4, 2024
1 parent 883c038 commit 0936e71
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ object JavaAsyncLoopRunner extends LoopRunner[Future] {
@tailrec
def finiteLoopAux(state: S): S = {
val newState = operation(state)
Thread.`yield`()
if (!terminateWhen(newState)) finiteLoopAux(newState)
else newState
}
Expand All @@ -61,6 +62,7 @@ object JavaAsyncLoopRunner extends LoopRunner[Future] {
def finiteLoopAux(state: S): S = {
val startTime = System.nanoTime()
val newState = operation(state)
Thread.`yield`()
if (!terminateWhen(newState)) {
val goalNanos = startTime + iterationNanos
val sleepNanos = goalNanos - startTime - busyLoopNanos
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package eu.joaocosta.minart.backend

import scala.annotation.tailrec
import scala.concurrent.*
import scala.scalanative.meta.LinktimeInfo.isMultithreadingEnabled
import scala.scalanative.concurrent.NativeExecutionContext.Implicits.queue
import scala.scalanative.libc.stdlib.*
import scala.scalanative.unsafe.{blocking as _, *}
import scala.scalanative.unsigned.*

import sdl2.all.*
import sdl2.enumerations.SDL_EventType.*

import eu.joaocosta.minart.runtime.*

Expand All @@ -17,8 +24,91 @@ object SdlAsyncLoopRunner extends LoopRunner[Future] {
terminateWhen: S => Boolean,
cleanup: () => Unit,
frequency: LoopFrequency
): Future[S] =
if (isMultithreadingEnabled)
SdlMultiThreadedAsyncLoopRunner.finiteLoop(initialState, operation, terminateWhen, cleanup, frequency)
else SdlSingleThreadedAsyncLoopRunner.finiteLoop(initialState, operation, terminateWhen, cleanup, frequency)
): Future[S] = {
val fullCleanup = () => {
cleanup()
SDL_Quit()
}
frequency match {
case LoopFrequency.Never =>
new NeverLoop(operation, fullCleanup).run(initialState)
case LoopFrequency.Uncapped =>
new UncappedLoop(operation, terminateWhen, fullCleanup).run(initialState)
case freq @ LoopFrequency.LoopDuration(_) =>
new CappedLoop(operation, terminateWhen, freq.millis, fullCleanup).run(initialState)
}
}

final class NeverLoop[S](operation: S => S, cleanup: () => Unit) {
def checkQuit(event: Ptr[SDL_Event]) =
SDL_WaitEvent(event) == 1 && SDL_EventType.define((!event).`type`) == SDL_QUIT

@tailrec
private def finiteLoopAux(event: Ptr[SDL_Event]): Unit = {
val quit = checkQuit(event)
Thread.`yield`()
if (quit) ()
else finiteLoopAux(event)
}

def run(initialState: S): Future[S] = {
val event: Ptr[SDL_Event] = malloc(sizeof[SDL_Event]).asInstanceOf[Ptr[SDL_Event]]
Future(operation(initialState))
.map { res =>
finiteLoopAux(event)
res
}
.map { res =>
free(event.asInstanceOf[Ptr[Byte]])
cleanup()
res
}
}
}

final class UncappedLoop[S](
operation: S => S,
terminateWhen: S => Boolean,
cleanup: () => Unit
) {
@tailrec
def finiteLoopAux(state: S): S = {
val newState = operation(state)
Thread.`yield`()
if (!terminateWhen(newState)) finiteLoopAux(newState)
else newState
}
def run(initialState: S): Future[S] = Future {
val res = finiteLoopAux(initialState)
cleanup()
res
}
}

final class CappedLoop[S](
operation: S => S,
terminateWhen: S => Boolean,
iterationMillis: Long,
cleanup: () => Unit
) {
@tailrec
def finiteLoopAux(state: S): S = {
val startTime = SDL_GetTicks().toLong
val newState = operation(state)
Thread.`yield`()
if (!terminateWhen(newState)) {
val endTime = SDL_GetTicks().toLong
val waitTime = iterationMillis - (endTime - startTime)
if (waitTime > 0) blocking { SDL_Delay(waitTime.toUInt) }
finiteLoopAux(newState)
} else newState
}

def run(initialState: S): Future[S] =
Future {
val res = finiteLoopAux(initialState)
cleanup()
res
}
}
}

This file was deleted.

This file was deleted.

0 comments on commit 0936e71

Please sign in to comment.