From 78cafe656684be32022bc9202628f9d3633e216d Mon Sep 17 00:00:00 2001 From: Alberto Torres Date: Thu, 29 Aug 2024 00:19:52 +0200 Subject: [PATCH] Add threading support to `LoadableResource`. --- libs/loadable/loadable.nim | 70 +++++++++++++++++++++++++++++++------- src/myou_engine.nim | 2 ++ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/libs/loadable/loadable.nim b/libs/loadable/loadable.nim index 3db1886..f2d2e3c 100644 --- a/libs/loadable/loadable.nim +++ b/libs/loadable/loadable.nim @@ -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,10 +73,28 @@ 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 - self.start_func(self) + 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: - self.onload_func(ok, err, p, len) + 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: diff --git a/src/myou_engine.nim b/src/myou_engine.nim index 70ed184..edb81d2 100644 --- a/src/myou_engine.nim +++ b/src/myou_engine.nim @@ -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: