Godot Version
4.2.1
Question
Let’s assume we have an autosave feature that works basically as follows:
- The game saving logic is contained within an autoloaded singleton
- In the
_ready
of that singleton, aThread
object is spun up which contains a while loop, using some thread-safe condition that is removed on_exit_tree
- It constantly checks for a “save trigger”. If a save trigger is received, it saves various resources to
user://
based on a save data dictionary (more on this later and my fundamental question about how to avoid busy waiting) - Various objects in the game will set save values in the save dictionary, based on events firing - either signals or just setting the dictionary’s values via the singleton accessor, which is thread-safe
I am aware this is the wrong way to do this, and fundamentally creates busy waiting.
Some representative code (assume d
is a Dictionary
, which I understand is inherently thread-safe according to this, assuming the dictionary size does not change).
var d := {}
var save_data := {}
func _ready() -> void:
thread = Thread.new()
thread.start(queue_thread)
func queue_thread() -> void:
while d.thread_is_alive:
if d.trigger_save:
d.trigger_save = false
ResourceSaver.save(...)
# convert save_data to JSON, whatever
# the point is we are doing the auto-saving here
OS.delay_msec(100)
func _exit_tree() -> void:
d.thread_is_alive = false
thread.wait_to_finish()
Clearly, the OS.delay_msec(100)
is a bandaid that reduces the issue of busy waiting on the thread.
What is the correct way to implement this? Resource saving clearly needs to be on a separate thread.
In my case, I have about 2-3 MB of save data that will need to be saved to the user://
system. One chunk of that data is actually saving an ArrayMesh
as a .tres
, so I need to very carefully make a duplicate of that mesh data prior to handing it over to the resource saver, to ensure the saver is not accessing the node on a separate thread (which understandably, crashes the engine).
I’m familiar with workflows in other langauges, such as BlockingQueue in Java / BlockingCollections in C#, or WaitOne.
Another example - in python we would call recv
on an open TCP socket connection in a while loop to read data on a separate thread - it sits there and waits until data appears rather than busy waiting. You can gracefully close the socket by forcing a new connection, which causes recv to return -1 or something like that, allowing the thread to cease.
Any help would be appreciated.