mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2026-03-21 19:56:14 +00:00
Break API calls in work towards moving all blocking io to ractors, dropped BackgroundWorker class
This commit is contained in:
@@ -16,6 +16,7 @@ class W3DHub
|
|||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
def self.on_thread(method, *args, &callback)
|
def self.on_thread(method, *args, &callback)
|
||||||
|
raise "Renew."
|
||||||
BackgroundWorker.foreground_job(-> { Api.send(method, *args) }, callback)
|
BackgroundWorker.foreground_job(-> { Api.send(method, *args) }, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -682,7 +682,23 @@ class W3DHub
|
|||||||
# mark MAIN ractor's task as started before handing off to background ractor
|
# mark MAIN ractor's task as started before handing off to background ractor
|
||||||
# so that we don't start up multiple tasks at once.
|
# so that we don't start up multiple tasks at once.
|
||||||
task.start
|
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
|
end
|
||||||
|
|
||||||
def task?(type, app_id, channel)
|
def task?(type, app_id, channel)
|
||||||
|
|||||||
@@ -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
|
|
||||||
96
lib/network_manager.rb
Normal file
96
lib/network_manager.rb
Normal file
@@ -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
|
||||||
9
lib/network_manager/api_client.rb
Normal file
9
lib/network_manager/api_client.rb
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
class W3DHub
|
||||||
|
class NetworkManager
|
||||||
|
# Api reimplemented in a Ractor friendly manner
|
||||||
|
class ApiClient
|
||||||
|
def initialize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,10 +6,12 @@ class W3DHub
|
|||||||
|
|
||||||
Store[:server_list] = []
|
Store[:server_list] = []
|
||||||
Store[:settings] = Settings.new
|
Store[:settings] = Settings.new
|
||||||
|
Store[:network_manager] = NetworkManager.new
|
||||||
Store[:application_manager] = ApplicationManager.new
|
Store[:application_manager] = ApplicationManager.new
|
||||||
Store[:ping_manager] = PingManager.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] = []
|
Store[:main_thread_queue] = []
|
||||||
|
|
||||||
@@ -34,9 +36,6 @@ class W3DHub
|
|||||||
while (block = Store.main_thread_queue.shift)
|
while (block = Store.main_thread_queue.shift)
|
||||||
block&.call
|
block&.call
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def needs_redraw?
|
def needs_redraw?
|
||||||
|
|||||||
@@ -112,7 +112,8 @@ require_relative "lib/broadcast_server"
|
|||||||
require_relative "lib/hardware_survey"
|
require_relative "lib/hardware_survey"
|
||||||
require_relative "lib/game_settings"
|
require_relative "lib/game_settings"
|
||||||
require_relative "lib/websocket_client"
|
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"
|
||||||
require_relative "lib/application_manager/manifest"
|
require_relative "lib/application_manager/manifest"
|
||||||
require_relative "lib/application_manager/status"
|
require_relative "lib/application_manager/status"
|
||||||
@@ -178,27 +179,18 @@ end
|
|||||||
|
|
||||||
logger.info(W3DHub::LOG_TAG) { "W3D Hub Linux Launcher v#{W3DHub::VERSION}" }
|
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..." }
|
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: 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: 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::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
|
# # Wait for BackgroundWorker to return
|
||||||
while W3DHub::BackgroundWorker.alive?
|
# while W3DHub::BackgroundWorker.alive?
|
||||||
W3DHub::BackgroundWorker.kill! if Gosu.milliseconds - worker_soft_halt >= 1_000
|
# W3DHub::BackgroundWorker.kill! if Gosu.milliseconds - worker_soft_halt >= 1_000
|
||||||
|
|
||||||
sleep 0.1
|
# sleep 0.1
|
||||||
end
|
# end
|
||||||
|
|
||||||
W3DHub::LOGGER&.close
|
W3DHub::LOGGER&.close
|
||||||
|
|||||||
Reference in New Issue
Block a user