This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
civilwargeeky's profile picture

Too long without yielding errors during file write

Started by civilwargeeky, 16 December 2013 - 10:41 PM
civilwargeeky #1
Posted 16 December 2013 - 11:41 PM
Hello. I am the coder for Variable Size Quarry, and recently I had a user come to me saying that my program errored during its backup file write with a "too long without yielding error." I have been told in the past that this happens most frequently on laggy servers, but I wanted to ask the pros if there is anything I can do.
The code for the function is here:
Spoiler
local function saveProgress(extras) --Session persistence
exclusions = { modem = true, }
if doBackup then
local toWrite = ""
for a,b in pairs(getfenv(1)) do
  if not exclusions[a] then
	  --print(a ,"   ", b, "   ", type(B)/>) --Debug
	if type(B)/> == "string" then b = "\""..b.."\"" end
	if type(B)/> == "table" then b = textutils.serialize(B)/> end
	if type(B)/> ~= "function" then
	  toWrite = toWrite..a.." = "..tostring(B)/>.."\n"
	end
  end
end
toWrite = toWrite.."doCheckFuel = false\n" --It has already used fuel, so calculation unnesesary
local file
repeat
  file = fs.open(saveFile,"w")
until file
file.write(toWrite) --=================Too long without yielding here===========================
if type(extras) == "table" then
  for a, b in pairs(extras) do
	file.write(a.." = "..tostring(B)/>)
  end
end
file.close()
end
end
This function is being calledmost often from this function
Spoiler
function mine(doDigDown, doDigUp, outOfPath,doCheckInv) -- Basic Move Forward
if doCheckInv == nil then doCheckInv = true end
if doDigDown == nil then doDigDown = true end
if doDigUp == nil then doDigUp = true end
if outOfPath == nil then outOfPath = false end
if inverted then
  doDigUp, doDigDown = doDigDown, doDigUp --Just Switch the two if inverted
end
if doRefuel and checkFuel() <= fuelTable[fuelSafety]/2 then
  for i=1, 16 do
  if turtle.getItemCount(i) > 0 then
	turtle.select(i)
	if checkFuel() < 200 + fuelTable[fuelSafety] then
	  turtle.refuel()
	end
  end
  end
end
local count = 0
while not forward(not outOfPath) do
  sleep(0) --Calls coroutine.yield to prevent errors
  count = count + 1
  if not dig() then
	attack()
  end
  if count > 10 then
	attack()
	sleep(0.2)
  end
  if count > 50 then
	if turtle.getFuelLevel() == 0 then --Don't worry about inf fuel because I modified this function
	  saveProgress({doCheckFuel = true})
	  error("No more fuel",0)
	elseif yPos > (startY-7) then --If it is near bedrock
	  bedrock()
	else --Otherwise just sleep for a bit to avoid sheeps
	  sleep(1)
	end
  end
end
checkSanity() --Not kidding... This is necessary
saveProgress(tab) --============================================RIGHT HERE====================
if doDigUp then
while turtle.detectUp() do
  sleep(0) --Calls coroutine.yield
  if not dig(true,turtle.digUp) then --This needs to be an absolute, because we are switching doDigUp/Down
	attackUp()
	count = count + 1
  end
  if count > 50 and yPos > (startY-7) then --Same deal with bedrock as above
	bedrock()
  end
  end
end
if doDigDown then
dig(true,turtle.digDown) --This needs to be absolute as well
end
percent = math.ceil(moved/moveVolume*100)
updateDisplay()
isInPath = (not outOfPath) --For rednet
if doCheckInv and careAboutResources then
if moved%invCheckFreq == 0 then
if isFull(16-keepOpen) then dropOff() end
end; end
if rednetEnabled then biometrics() end
end
--Insanity Checking
function checkSanity()
  if isInPath and not (facing == 0 or facing == 2) and #events == 0 then --If mining and not facing proper direction and not in a turn
	turnTo(0)
	rowCheck = "right"
  end
  if xPos < 0 or xPos > x or zPos < 0 or zPos > z or yPos < 0 then
	saveProgress()
	print("Oops. Detected that quarry was outside of predefined boundaries.")
	print("Please go to my forum thread and report this with a short description of what happened")
	print("If you could also run \"pastebin put Civil_Quarry_Restore\" and give me that code it would be great")
	error("",0)
  end
end
I don't understand why the too long without yielding error is coming up. I am calling "sleep(0)" every time the turtle moves, because I was told that sleep calls "coroutine.yield()", but yet it still errors sometimes. So what could I do? I'm not too knowledgeable about coroutines and how they work internally. The parallel API is as far as I've needed to go.
Any help would be appreciated. Thank you for reading.
Edited on 16 December 2013 - 10:43 PM
Grim Reaper #2
Posted 16 December 2013 - 11:56 PM
My first guess would be that the handle to 'file' cannot be obtained because it there is another handle to it that hasn't been closed. If this is the case, then you would just be repeating the same loop infinitely super fast (no yielding) and thus this error would appear. So, I guess to fix it would just to be to handle the case that you couldn't save to that file.
awsmazinggenius #3
Posted 17 December 2013 - 12:12 AM
If sleep(0) isn't working, try this:

os.startTimer(0)
os.pullEvent("timer")
The timer ID handling really shouldn't matter, unless you are using timers elsewhere in your program. (I haven't looked at the code yet.)
Grim Reaper #4
Posted 17 December 2013 - 12:43 AM
If sleep(0) isn't working, try this:

os.startTimer(0)
os.pullEvent("timer")
The timer ID handling really shouldn't matter, unless you are using timers elsewhere in your program. (I haven't looked at the code yet.)

This is essentially the same thing as calling sleep(0) except for the timer ID handling that you mentioned. I don't see how this would change anything?
civilwargeeky #5
Posted 17 December 2013 - 12:47 AM
My first guess would be that the handle to 'file' cannot be obtained because it there is another handle to it that hasn't been closed. If this is the case, then you would just be repeating the same loop infinitely super fast (no yielding) and thus this error would appear. So, I guess to fix it would just to be to handle the case that you couldn't save to that file.
Hmm… Why would there be another file handle open though?
repeat
  file = fs.open(saveFile, "w")
until file
That should only make one file handle, as the file handle existing breaks the loop. I don't really see how it could continue looping.
And what do you mean by "handle the case that you couldn't save to file"? I don't think I'm able to check if the file was successfully closed (if thats what you're saying). Sorry, but I'm just a bit confused.
Edited on 16 December 2013 - 11:47 PM
Lyqyd #6
Posted 17 December 2013 - 12:58 AM
If the file handle fails to open, you shouldn't just continue to stupidly bash your head against whatever problem is preventing it, you should read out the error message and get some user interaction. Maybe try it again after a moment, but don't just sit there looping trying to open the handle.
civilwargeeky #7
Posted 17 December 2013 - 01:04 AM
stupidly bash your head against whatever problem is preventing it
Ouch. That's not nice…

Regardless, point taken and thank you, I was unaware that fs.open even gave error messages. I will look into fixing it. Do you think that fixing that will solve the "too long without yielding error"? Or is there something else I should be doing that I'm not?
theoriginalbit #8
Posted 17 December 2013 - 01:25 AM
I was unaware that fs.open even gave error messages.
Of course it does, about 80% of Lua functions do. The second return value is the error message when the first value is false or nil.
Bomb Bloke #9
Posted 17 December 2013 - 01:28 AM
I am calling "sleep(0)" every time the turtle moves, because I was told that sleep calls "coroutine.yield()", but yet it still errors sometimes.
On that note, most of the commands in the turtle API also yield. The exceptions seem to be turtle.getItemCount(), turtle.getItemSpace(), and turtle.getFuelLevel().