`ll.Sleep(0.1)` Followed by `LLEvents:on('event', func)` triggering "Failed to perform mandatory yield"
in progress
LiamHoffen Resident
- Modify the LLEvents table, calling :on or :off
- Follow that with function call with a forced delay (ll.Sleep, ll.InstantMessage, etc.)
- Modify the LLEvents table again
The 2nd modification of the events table triggers a yield error, because in step 2 we executed a call with a forced delay. This could be the ll.Sleep, or any ll.* function described as having a forced delay.
LLEvents:on("touch_start", print)
ll.Sleep(0.001)
LLEvents:on("touch_start", print)
The error generated is:
Object [script:New Script] Script run-time error
runtime error
Failed to perform mandatory yield
lua_script:3
Log In
SuzannaLinn Resident
Another example with "Failed to perform mandatory yield":
LLEvents:on("touch_start",print)
ll.SetText("hello",vector(1,1,1),1)
LLEvents:off("touch_start",print)
This works without error:
LLEvents:on("touch_start",print)
ll.SetText("hello",vector(1,1,1),1)
; (function() end)()
LLEvents:off("touch_start",print)
H
Harold Linden
SuzannaLinn Resident: Thanks!
LOP_CALL
tail checks for interrupts might not be working in the way that I thought at all then. It should realize at the end of handling ll.SetText()
that it's run out of time and should yield.SuzannaLinn Resident
Harold Linden
The issue seems to be only with two LLEvents, as far as we have found.
These two ones work well without error:
LLEvents:on("touch_start",print)
ll.SetText("hello",vector(1,1,1),1)
LLTimers:on(3,print)
LLTimers:on(3,print)
ll.SetText("hello",vector(1,1,1),1)
LLEvents:on("touch_start",print)
SuzannaLinn Resident
Harold Linden
No, not only with two LLEvents.
This example from a solved canny, https://feedback.secondlife.com/slua-alpha/p/a-call-to-llsettext-triggers-errors-in-immediately-following-metatable-index-cal, works well. no error:
Test=setmetatable({},{ __newindex = function() end })
ll.SetRot(ZERO_ROTATION)
Test[1]=1
But adding a LLEvents in between, it throws the "Failed to perform mandatory yield":
Test=setmetatable({},{ __newindex = function() end })
LLEvents:once("touch_start",print)
ll.SetRot(ZERO_ROTATION)
Test[1]=1
H
Harold Linden
SuzannaLinn Resident: Okay so this turned out to be much weirder than I expected. I traced through all the SLua code and couldn't see any issues with my interrupt logic.
Turns out it's some code in the outer edges of the SL-specific stuff that's only really manually testable. In 2006 a throttle was added to force-sleep scripts for a short time when they changed their touch handlers to prevent people's cursors flickering constantly as they hovered over objects.
Basically, in LSL flipping between a state that has a touch handler and one that doesn't will incur a 0.25s sleep penalty.
Unfortunately that sleep happens as we _re-enter_ the script, and our extensions to Luau can't necessarily yield at all of the places they can resume from (like yields after the tail of a function call). It was trying to interrupt to service that forced-sleep due to the addition of the touch handler, but resumed in a place we can't immediately re-yield from.
I moved that sleep addition to after the execution happens rather than before, should fix it once it's deployed.
H
Harold Linden
Ah, I think I know what this is. I had to add interrupt checks when
__index
metamethods get called on objects (like would happen when resolving LLEvents:on
is resolved), and the sleep support has curious behavior where it doesn't try to interrupt, it tries to force the _next_ function call to trigger an interrupt in the LOP_CALL
interrupt check.Because the next interrupt that actually triggers is inside
LOP_GETTABLE
where we're not allowed to yield, it triggers the "Failed to perform mandatory yield" error.I thought my changes to the VM to allow safely trying an interrupt after a C function finishes would have handled that, but clearly not.
H
Harold Linden
marked this post as
in progress
Thanks for the report! I'm having a look at this, probably some strange interaction with the interrupt hooks I added in the
LLEvents
code.