Event Handler Timeout Makes Calling Thread To Timeout As Well

I’ve a couple of questions about the following behavior that I discovered today. Consider the code below:

print("Thread 1 start")

local function normalScript()

print("Thread 3 start")

print("Thread 3 finished")

end

local function timeoutScript()

print("Thread 2 start")

while true do

end

print("Thread 2 finished")

end

local bindable = Instance.new("BindableEvent")

-- This will be processed last

bindable.Event:Connect(function()

normalScript()

end)

-- This will be processed first

bindable.Event:Connect(function()

timeoutScript()

end)

bindable:Fire()

print("Thread 1 finished")

If you run this code, not only the Thread 2 will error due timeout, but thread 1 and thread 3 as well. The end result is that an event handler is capable of halting the calling thread and other event handler that was supposed to execute afterwards. I find this behavior counter intuitive because according with the documentation:

BindableEvent Theading Behavior

This code sample demonstrates how BindableEvents, like all Events, create threads of each connected function. Even if one errors, like ohNo does, the others continue. In addition, this sample shows how functions connected to the event can yield without blocking the thread that fired the event.

It appears that right now (no deferred events), while the calling thread is waiting for all event handlers to yield and return control to it, it’s script timer still counts and the end result is that the calling thread and the event handlers that did not started being processed yet also error with timeout.

My two questions about this situation are:

  1. Is there any official documentation or even a devforum discussion on the subject (couldn’t find any for myself)
  2. What will happen once deferred events kick in? I supposed the calleing thread will not error anymore because the event is deferred. But, events that were supposed to be processed after the timeout event will still throw error as well?

Maybe your thread 2 is timing out because you forgot to add a Wait() to it? Also is that the code you’re running? Because you need to parent the bindable somewhere. Try looking into coroutines if you want to achieve a sort of multithreading too

This makes sense since when a timeout occurs the whole script is stopped. Hence the error ‘Script passed max execution time’.

1 Like

Thread 2 is timing out out on purpose to illustrate the bug. In theory, if a script fire a bindable event, the calling thread should not error neither other event handlers connected to the same event should be affected by an error in an event handler. That’s one of the main points of calling another thread to run the code instead of using a function. However, I recently found out that if the event handler timeouts (which throws an error), it affects other event handlers and the caller and they also throw an error. The code above (that has no purpose) is just a simple reproduction of this bug. If you replace the infinite loop by an "error(“This is an error”), then thread 3 will run and the calling thread (thread 1) will finish, which is the expected behavior for a thread.

You don’t need to parent bindable to make it work. Creating a bindable, connect to an event handler, firing and destroying is a common pattern to implement fast spawn (that will be deprecated in the near future due to deferred events). An example of this pattern in the Quenty Nervermore Engine: NevermoreEngine/Binder.lua at main · Quenty/NevermoreEngine · GitHub (Line 79)

1 Like