From f98d8c339494478834ec77e8f3cd7352928e7179 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Fri, 30 Jan 2026 19:36:11 -0600 Subject: [PATCH] Break API calls in work towards moving all blocking io to ractors, dropped BackgroundWorker class --- lib/api.rb | 1 + lib/application_manager.rb | 18 ++- lib/background_worker.rb | 186 ------------------------------ lib/network_manager.rb | 96 +++++++++++++++ lib/network_manager/api_client.rb | 9 ++ lib/window.rb | 7 +- w3d_hub_linux_launcher.rb | 24 ++-- 7 files changed, 134 insertions(+), 207 deletions(-) delete mode 100644 lib/background_worker.rb create mode 100644 lib/network_manager.rb create mode 100644 lib/network_manager/api_client.rb diff --git a/lib/api.rb b/lib/api.rb index d8551c4..1d33121 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -16,6 +16,7 @@ class W3DHub ].freeze def self.on_thread(method, *args, &callback) + raise "Renew." BackgroundWorker.foreground_job(-> { Api.send(method, *args) }, callback) end diff --git a/lib/application_manager.rb b/lib/application_manager.rb index 8c9d846..5b663aa 100644 --- a/lib/application_manager.rb +++ b/lib/application_manager.rb @@ -682,7 +682,23 @@ class W3DHub # mark MAIN ractor's task as started before handing off to background ractor # so that we don't start up multiple tasks at once. task.start - BackgroundWorker.ractor_task(task) + on_ractor(task) + end + + def on_ractor(task) + raise "Something has gone horribly wrong!!!" unless Ractor.main? + + ractor = Ractor.new(task) do |t| + t.start + end + + Thread.new do + while (message_event = ractor.take) + break unless message_event.is_a?(Task::MessageEvent) + + Store.application_manager.handle_task_event(message_event) + end + end end def task?(type, app_id, channel) diff --git a/lib/background_worker.rb b/lib/background_worker.rb deleted file mode 100644 index b845a81..0000000 --- a/lib/background_worker.rb +++ /dev/null @@ -1,186 +0,0 @@ -class W3DHub - class BackgroundWorker - LOG_TAG = "W3DHub::BackgroundWorker" - @@instance = nil - @@alive = false - - def self.create - raise "BackgroundWorker instance already exists!" if @@instance - logger.info(LOG_TAG) { "Starting background job worker..." } - - - @@thread = Thread.current - @@alive = true - @@run = true - @@instance = self.new - - @@instance.handle_jobs - end - - def self.instance - @@instance - end - - def self.run? - @@run - end - - def self.alive? - @@alive - end - - def self.busy? - instance&.busy? - end - - def self.shutdown! - @@run = false - end - - def self.kill! - @@thread.kill - - @@instance.kill! - end - - def self.job(job, callback, error_handler = nil, data = nil) - @@instance.add_job(Job.new(job: job, callback: callback, error_handler: error_handler, data: data)) - end - - def self.parallel_job(job, callback, error_handler = nil, data = nil) - @@instance.add_parallel_job(Job.new(job: job, callback: callback, error_handler: error_handler, data: data)) - end - - def self.foreground_job(job, callback, error_handler = nil, data = nil) - @@instance.add_job(Job.new(job: job, callback: callback, error_handler: error_handler, deliver_to_queue: true, data: data)) - end - - def self.foreground_parallel_job(job, callback, error_handler = nil, data = nil) - @@instance.add_parallel_job(Job.new(job: job, callback: callback, error_handler: error_handler, deliver_to_queue: true, data: data)) - end - - def self.ractor_task(task) - raise "Something has gone horribly wrong!!!" unless Ractor.main? - - ractor = Ractor.new do - t = Ractor.receive - - t.start - end - - ractor.send(task) - - Thread.new do - while (message_event = ractor.take) - break unless message_event.is_a?(W3DHub::ApplicationManager::Task::MessageEvent) - - Store.application_manager.handle_task_event(message_event) - end - end - end - - def initialize - @busy = false - @jobs = [] - - # Jobs which are order independent - @parallel_busy = false - @thread_pool = [] - @parallel_jobs = [] - end - - def kill! - @thread_pool.each(&:kill) - - logger.info(LOG_TAG) { "Forcefully killed background job worker." } - @@alive = false - end - - def handle_jobs - 8.times do |i| - Thread.new do - @thread_pool << Thread.current - - while BackgroundWorker.run? - job = @parallel_jobs.shift - - @parallel_busy = true - - begin - job&.do - rescue => e - job&.raise_error(e) - end - - @parallel_busy = !@parallel_jobs.empty? - - sleep 0.1 - end - end - end - - Thread.new do - @thread_pool << Thread.current - - while BackgroundWorker.run? - job = @jobs.shift - - @busy = true - - begin - job&.do - rescue => e - job&.raise_error(e) - end - - @busy = !@jobs.empty? - - sleep 0.1 - end - - logger.info(LOG_TAG) { "Stopped background job worker." } - @@alive = false - end - end - - def add_job(job) - @jobs << job - end - - def add_parallel_job(job) - @parallel_jobs << job - end - - def busy? - @busy || @parallel_busy - end - - class Job - def initialize(job:, callback:, error_handler: nil, deliver_to_queue: false, data: nil) - @job = job - @callback = callback - @error_handler = error_handler - @deliver_to_queue = deliver_to_queue - @data = data - end - - def do - result = @data ? @job.call(@data) : @job.call - deliver(result) - end - - def deliver(result) - if @deliver_to_queue - Store.main_thread_queue << -> { @callback.call(result) } - else - @callback.call(result) - end - end - - def raise_error(error) - logger.error error - @error_handler&.call(error) - end - end - end -end diff --git a/lib/network_manager.rb b/lib/network_manager.rb new file mode 100644 index 0000000..d44596d --- /dev/null +++ b/lib/network_manager.rb @@ -0,0 +1,96 @@ +class W3DHub + # all http(s) requests for API calls and downloading images run through here + class NetworkManager + NetworkEvent = Data.define(:context, :result) + Request = Struct.new(:context, :callback) + Context = Data.define( + :request_id, + :url, + :headers, + :body, + :bearer_token + ) + + def initialize + @requests = {} + + @ractor = Ractor.new do + raise "Something has gone quite wrong!" if Ractor.main? + + queue = [] + api_client = ApiClient.new + + # Ractor has no concept of non-blocking send/receive... :cry: + Thread.new do + while (context = Ractor.receive) # blocking + # we cannot (easily) ensure we always are receive expected data + next unless context.is_a?(Context) + + queue << context + end + end + + Async do + loop do + context = queue.shift + + # goto sleep for an instant if there is no work to be doing + unless context + sleep 0.1 + next + end + + Sync do + result = api_client.handle(context) + + Ractor.yield(NetworkEvent.new(context, result)) + end + end + end + end + + monitor + end + + def add_request(url, headers, body, bearer_token, &block) + request_id = SecureRandom.hex + + @requests << Request.new( + Context.new( + request_id, + url, + headers, + body, + bearer_token + ), + block + ) + + @ractor.send(context) + + request_id + end + + def monitor + raise "Something has gone quite wrong!!!" unless Ractor.main? + + # Thread that spends its days sleeping **yawn** + Thread.new do + while (event = @ractor.take) + pp event + + next unless event.is_a?(NetworkEvent) + + request = @request.find { |r| r.context.request_id == event.context.request_id } + + next if request + + @requests.delete(request) + result = event.result + + Store.main_thread_queue << ->(result) { request.callback(result) } + end + end + end + end +end diff --git a/lib/network_manager/api_client.rb b/lib/network_manager/api_client.rb new file mode 100644 index 0000000..917bfe1 --- /dev/null +++ b/lib/network_manager/api_client.rb @@ -0,0 +1,9 @@ +class W3DHub + class NetworkManager + # Api reimplemented in a Ractor friendly manner + class ApiClient + def initialize + end + end + end +end diff --git a/lib/window.rb b/lib/window.rb index 49ace88..21c392e 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -6,10 +6,12 @@ class W3DHub Store[:server_list] = [] Store[:settings] = Settings.new + Store[:network_manager] = NetworkManager.new Store[:application_manager] = ApplicationManager.new Store[:ping_manager] = PingManager.new - BackgroundWorker.parallel_job(-> { Async { |task| Store.ping_manager.monitor(task) } }, nil) + # FIXME + # BackgroundWorker.parallel_job(-> { Async { |task| Store.ping_manager.monitor(task) } }, nil) Store[:main_thread_queue] = [] @@ -34,9 +36,6 @@ class W3DHub while (block = Store.main_thread_queue.shift) block&.call end - - # Manually sleep main thread so that the BackgroundWorker thread can be scheduled - sleep(update_interval / 1000.0) if W3DHub::BackgroundWorker.busy? || Store.application_manager.busy? end def needs_redraw? diff --git a/w3d_hub_linux_launcher.rb b/w3d_hub_linux_launcher.rb index 5d196c3..d30fdf2 100644 --- a/w3d_hub_linux_launcher.rb +++ b/w3d_hub_linux_launcher.rb @@ -112,7 +112,8 @@ require_relative "lib/broadcast_server" require_relative "lib/hardware_survey" require_relative "lib/game_settings" require_relative "lib/websocket_client" -require_relative "lib/background_worker" +require_relative "lib/network_manager" +require_relative "lib/network_manager/api_client" require_relative "lib/application_manager" require_relative "lib/application_manager/manifest" require_relative "lib/application_manager/status" @@ -178,27 +179,18 @@ end logger.info(W3DHub::LOG_TAG) { "W3D Hub Linux Launcher v#{W3DHub::VERSION}" } -Thread.new do - W3DHub::BackgroundWorker.create -end - -until W3DHub::BackgroundWorker.alive? - sleep 0.1 -end - logger.info(W3DHub::LOG_TAG) { "Launching window..." } # W3DHub::Window.new(width: 980, height: 720, borderless: false, resizable: true).show unless defined?(Ocra) W3DHub::Window.new(width: 1280, height: 800, borderless: false, resizable: true).show unless defined?(Ocra) # W3DHub::Window.new(width: 1920, height: 1080, borderless: false, resizable: true).show unless defined?(Ocra) -W3DHub::BackgroundWorker.shutdown! -worker_soft_halt = Gosu.milliseconds +# worker_soft_halt = Gosu.milliseconds -# Wait for BackgroundWorker to return -while W3DHub::BackgroundWorker.alive? - W3DHub::BackgroundWorker.kill! if Gosu.milliseconds - worker_soft_halt >= 1_000 +# # Wait for BackgroundWorker to return +# while W3DHub::BackgroundWorker.alive? +# W3DHub::BackgroundWorker.kill! if Gosu.milliseconds - worker_soft_halt >= 1_000 - sleep 0.1 -end +# sleep 0.1 +# end W3DHub::LOGGER&.close