A call to ll.SetText triggers errors in immediately following metatable __index calls
tracked
SungAli Resident
Second Life Project Lua Editor 7.1.12.14888088240 (64bit)
I created a short script demonstrating multiple inheritance in SLua. The program worked exactly as intended until I added a call to ll.SetText at the beginning of the script, whereupon the script produced an incomprehensible "Failed to perform mandatory yield" error when the __index function was invoked.
Attached is a stripped down version of the script demonstrating the problem, together with various commented out alternatives trying to narrow the problem down. Details of trying the alternatives, and the full error message are included in the comments at the end of the script.
Log In
H
Harold Linden
Thanks for sending this in!
This is definitely a bug and we'd like to improve behavior here, though the underlying issue is a bit complex.
For context, SLua works by allowing Lua scripts to run small slices of code before injecting a yield (well, break, but similar to a yield) after a certain amount of time so that other scripts' code can run. These yield time checks use Luau's
VM_INTERRUPT()
construct and happen whenever a function call entered, when a return
opcode is hit, and at the back edge of the various looping constructs.The complicating factors are that:
- These yield checks only happen when a functions is _entered_, not when it is _exited_ if the function is written in C++.
- Many ll.functions that work with prims are slow enough that they actually take longer than the script's time slice (this is also the case in Mono, as the functions share C++ implementations.)
- The simulator cannot inject a yield when a script is inside an __indexmetamethod, or any other metamethod other than__calldue to https://luau.org/compatibility#lua-52 (see "yieldable metamethods" and the notes below the table)
- When we run into a situation where we _need_ to yield to keep the sim responsive, but we cannot safely inject a yield, we instead throw an "unable to perform mandatory yield" error to get us back to a place in the code where we can yield (or let the script explode)
In the situations below, the script likely already run out of time due to the runtime of an
ll.*()
call just before the Test[1]
, but hasn't hit a place where it can do the yield check yet. The first place it hits a yield check is the function call inside __index
which makes it explode because the timeslice has already been overrun so much that we've decided to make the yield mandatory.It seems to me that we can improve behavior by doing yield checks at the _end_ of every C++ function call as well, so we're more likely to know we've run out of time in a place where we can safely inject a yield, and not potentially explode the script by doing the check later.
As a side note though this isn't happening here, putting too much complex logic in metamethods like
__index
is a potential pitfall as they aren't yieldable. They should be kept as trivial as possible unless we can figure out a way to make the Luau VM allow yielding inside them.H
Harold Linden
Also agreed that "unable to perform mandatory yield" is a bit vague. We'll write up a better error message there, and include some pointers about what the actual issue is and how to avoid it once we've got the docs figured out.
H
Harold Linden
tracked
SuzannaLinn Resident
also a "Failed to perform mandatory yield" with:
Test=setmetatable({},{ __index = function() return 0 end })
ll.SetPrimitiveParams({PRIM_NAME,"testing"})
print(Test[1])
or with:
Test=setmetatable({},{ __index = function() return 0 end })
_ = ll.GetPrimitiveParams({PRIM_NAME})
print(Test[1])
or with:
Test=setmetatable({},{ __newindex = function() end })
ll.SetRot(ZERO_ROTATION)
Test[1]=1
no error with SetObjectName or GetObjectName
no error with __index set to a table
Janet Rossini
Good catch, Ali and Suzanne! And thanks Harold!
Do we think that the issue comes down to
__index
being a function, and that having it be a table will not cause the problem, and that the connection to ll.SetPrimitiveParams (and its cousins) is NOT a sign that there is issue with those related functions?llSLPPF, as we fondly call it, is critical to most every non-physical moving object in SL, so it really needs to work.
Harold, we Valkyries have a lot of trains and trams and such that use llSLPPF. Do you think we need to prioritize testing that capability in SLua? We've been assuming that our redesign of our movers could rely on that function working well, but we can probably reprioritize our work to bear down on it a bit if you think it's particularly needed.
SuzannaLinn Resident
Until Harold solves it, the current workaround is to insert something that includes a yield time check, for instance:
Test=setmetatable({},{ __index = function() return 0 end })
ll.SetPrimitiveParams({PRIM_NAME,"testing"})
;(function() end)()
print(Test[1])
or the same, in a more beautiful style:
YIELD = function() end
Test=setmetatable({},{ __index = function() return 0 end })
ll.SetPrimitiveParams({PRIM_NAME,"testing"})
YIELD()
print(Test[1])