Large delay after file:write()

I made a simple script to write JSON data to a file, then read it after it has been written:

local json = require("json")

local filename = "save.json"
local path = system.pathForFile(filename, system.DocumentsDirectory)

local data = {
	thing = 3,
	otherthing = "whatup"
}

local function save()
	local file, err = io.open(path, "w")
	if file then
		local json = json.encode(data, { indent = true })
		file:write(json)
	else
		print("ERROR: " .. err)
	end
end

local function load()
	local file, err = io.open(path, "r")
	if file then
		local contents = file:read("*a")
		print("Contents: " .. contents)
		io.close(file)
	end
end

save()
timer.performWithDelay(10000, load)

If I try to save this file and load it immediately afterwards without the timer, it will print an empty string. It turns out that after the file is written to, it stays empty for several seconds before the contents of the file appear and I can read it.

I tested this in the Corona Simulator. I’d assume the delay is smaller on a real device, but I’m concerned about edge cases where the player tries to read the data too early and thinks there is no data.

Is this the correct way to handle save data? If not, how do you do it?

That doesn’t look fundamentally different to how I save files, and we save frequently throughout gameplay (and with considerably more data than that). It’s practically instantaneous in the sim for us.

The only difference I can see is that we call io.close(file) after file:write(json) - it could be that the file is remaining open for a few seconds and cannot be read correctly.

1 Like

The reason is that writing is an asynchronous function that occurs on a different thread, if I’ve understood correctly.

While the writing process takes a fraction of a second in most cases, if you attempt to read the file right away, it’s still being written using a different thread.

Yup, the lack of io.close(file) was the problem. It writes immediately now, thanks for pointing that out. I can print the contents by call load() immediately after save() too.

Ah, this means I still need to be careful not to read immediately after saving, then. There’s a very small amount of save data in my game so I think this should be fine.

I would recommend changing the logic so you only need to read from the data file once, if possible.
If you read the data when the app starts up and then store that data in a table that persists throughout the session, you can just look up the value in that table as and when you need to.
When changes are made to the data table you can write it to the file, but you won’t need to read it back from the file again because the data is already present in the app.

1 Like

Hi,

Unless your code is just proof-of-concept, then as @alanFlickGames noted, you just need to load the json once. You can just keep a simple data table that is json encodable, and add/pull from that and save as needed. Always try and keep io stuff to a minimum if possible.

-dev

If you modify the table then write the json-encoded file often, you could end up draining the user’s battery. Also, the frequent writing may slow your game/app.

So, you might consider the trick I used when making the persistent data helper:
https://roaminggamer.github.io/RGDocs/pages/SSK2/libraries/persist/

  • Make a wrapper to modify the table.
  • Every time the wrapper is called (to modify the table) a timer is (re)set.
  • When the timer elapses, the table gets saved.

This way, if you modify the table once, and then again within the timer period, you only get one file write instead of two. Of course, the same is true for three mods, four mods, etc. Until the timer elapses, you do no writes.

The only danger here is if the game/app/device crashes during the magical ‘deferred write’ window, but the odds of that are pretty rare.

3 Likes