Add threading support to LoadableResource.

This commit is contained in:
Alberto Torres 2024-08-29 00:19:52 +02:00
parent e748831f3a
commit 78cafe6566
2 changed files with 60 additions and 12 deletions

View file

@ -58,12 +58,14 @@ type LoadableResource* = ref object of RootObj
done_func: proc()
cancel_func: proc()
str*: proc(): string
use_threads: bool
result: ref Result # only to be used with loadAll
proc newLoadableResource*[T: LoadableResource](
start: proc(self: LoadableResource),
done: proc() = nil,
str: proc(): string = nil,
use_threads = false,
): T =
new(result)
result.start_func = start
@ -71,11 +73,29 @@ proc newLoadableResource*[T: LoadableResource](
result.str = str
if str == nil:
result.str = proc(): string = ""
result.use_threads = use_threads
# main -> thread channels
var to_start: Channel[LoadableResource]
# main <- thread channels
var to_return: Channel[(LoadableResource, bool, string, pointer, int)]
var to_done: Channel[LoadableResource]
proc start*[T: LoadableResource](self: T) =
self.status = Started
if self.use_threads:
to_start.send self
else:
self.start_func(self)
proc doneImpl*[T: LoadableResource](self: T) =
if self.status == Started:
self.cancel()
else:
self.status = NotStarted
if self.done_func != nil:
self.done_func()
proc `onload=`*[T: LoadableResource](self: T, onload_func: proc(ok: bool, err: string, p: pointer, len: int)) =
self.onload_func = onload_func
@ -85,25 +105,24 @@ proc onload*[T: LoadableResource](self: T, ok: bool, err: string, p: pointer, le
if self.result != nil:
self.result[] = (ok, err, p, len)
if self.onload_func != nil:
if self.use_threads:
to_return.send((self.LoadableResource, ok, err, p, len))
else:
self.onload_func(ok, err, p, len)
else: # cancelled
self.status = NotStarted
if self.done_func != nil:
self.done_func()
self.doneImpl()
# TODO: check if we can always use destructors instead of calling this
proc done*[T: LoadableResource](self: T) =
if self.status == Started:
self.cancel()
if self.use_threads:
to_done.send self
else:
self.status = NotStarted
if self.done_func != nil:
self.done_func()
self.doneImpl()
proc cancel*[T: LoadableResource](self: T) =
if self.status != Started:
return
if self.cancel_func != nil:
if self.cancel_func == nil:
# self.`onload=`(proc(ok, err, p, len: auto) =
# self.done())
self.onload_func = proc(ok: bool, err: string, p: pointer, len: int) =
@ -128,6 +147,31 @@ proc cancel*[T: LoadableResource](self: T) =
# results.add res.result[]
# res.result = nil
var worker: Thread[void]
proc workerThreadProc() {.thread.} =
while true:
let res = to_start.recv()
if res == nil:
break
cast[proc(self: LoadableResource) {.gcsafe.}](res.start_func)(res)
worker.createThread(workerThreadProc)
to_start.open()
to_return.open()
to_done.open()
proc updateLoadableWorkerThreads*() =
while true:
let tried = to_return.tryRecv()
if not tried.dataAvailable:
break
let (res, ok, err, p, len) = tried.msg
res.onload_func(ok, err, p, len)
while true:
let tried = to_done.tryRecv()
if not tried.dataAvailable:
break
tried.msg.done_func()
type Fetch* = ref object of LoadableResource
custom: pointer
@ -226,6 +270,7 @@ proc loadUri*(
var done_func: proc()
var self: Fetch
var uri = uri
var use_threads = false
when not defined(onlyLocalFiles):
when defined(emscripten):
const is_remote = true
@ -261,6 +306,7 @@ proc loadUri*(
discard emscripten_fetch_close(cast[ptr emscripten_fetch_t](self.custom))
self.custom = nil
else:
use_threads = true
var client = newHttpClient()
var response: string
start_func = proc(self: LoadableResource) =
@ -298,7 +344,7 @@ proc loadUri*(
if cast[Fetch](self).auto_done:
self.done()
proc str(): string = uri
self = newLoadableResource[Fetch](start_func, done_func, str)
self = newLoadableResource[Fetch](start_func, done_func, str, use_threads=use_threads)
self.onload_func = onload_func
self.auto_done = auto_done
if auto_start:

View file

@ -77,6 +77,7 @@ import ./screen
import ./platform/platform
import ./loaders/blend
import ./util
from loadable import updateLoadableWorkerThreads
import arr_ref
export arr_ref
@ -165,6 +166,7 @@ proc myou_main_loop*(self: MyouEngine) =
##
## You usually don't need to call this. Use `run <#run,MyouEngine>`_
## instead.
updateLoadableWorkerThreads()
# TODO: make a table object that can be iterated while changing, e.g. with a
# seq and a dirty flag to update the seq
if self.new_scenes.len != 0: