SetAsync not able to run when spawning meltable threads

I am trying to save all players data when the server shuts down. The code always stops before it can save the data. It usually stops when it prints 1 but some times it stops before that. I know it works if I do not use task.spawn but I need to use it so if it fail to save data for one player it can save the data of the others. I could not find any solutions on the Developer Hub?

function Save(player)
	if sessionData[player.userId] then
		local success = nil
		local errorMsg = nil
		local attempt = 1
		
		repeat
			success, errorMsg = pcall(function()
				print(1)
				database:SetAsync(player.userId, sessionData[player.userId])
				print(2)
			end)

			attempt += 1 -- repeats 5 up to five times with a 3 second delay if it fails to save the data
			if not success then
				warn(errorMsg)
				task.wait(3)
			end
		until success or attempt >= 5
		
		if success then
			print("Data saved for", player, player.userId)
		else
			warn("Unable to save data for", player, player.userId)
		end
	end
end
--Players.PlayerRemoving:Connect(Save)

game:BindToClose(function()
	for _,player in pairs(Players:GetPlayers()) do
		task.spawn(function()
			Save(player)
		end)
	end
end)

Inside of the game:BindToClose function you need to yield the main thread until all players have saved. task.spawn creates an extraneous thread so the function is not yielding, thus it’s telling the game that it’s safe to shut down.

Something like this should work:

game:BindToClose(function()
	local players = Players:GetPlayers()
	local playerCount = #players
	local playersSaved = 0

	for _,player in players do
		task.spawn(function()
			Save(player)
			playersSaved += 1
		end)
	end

	while playersSaved < playerCount do -- actually yield until all players are saved
		task.wait() -- while _ do task.wait is kind of gross (events may be more suitable here) but it should work
	end
end)
1 Like

Thank you so much, although I am still a little confused. If you could explain in laymen terms how anding task.wait fixes it that would mean the world to me.

Basically when the server is shut down, it calls every function that is bound via :BindToClose. The server then waits until every bound function is done running (or times out after 30 seconds) before the server decides it’s ok to shut down.

Because task.spawn does not yield the thread (function) in which it is called, the function bound to game:BindToClose never yields, so it tells the server it is ok to shut down.

Adding task.wait() yields the thread in which it is called, thus when you add the while _ do task.wait(), the thread will repeatedly yield (the function won’t finish running) until all the players’ data have saved.

So in layman’s terms, it’s basically saying “while there are players whose data hasn’t been saved yet, push back the server’s scheduled shutdown time”

1 Like

Thank again that cleared it up.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.