Event BasePart.Touched is not safe to access in parallel

Hello ,

As you can see in the topic … I encounter this error . But idk how to solve the problem . Any help is appriciated :slight_smile: :slight_smile:

here is the script.

local mobmod = require(game.ReplicatedStorage.Module.MobModule)
local Fast = require(game.ReplicatedStorage.Module.FastWait)

local folder = workspace.Folder

local runConnection
local playerAddedConnection
local childConnection
local parentConnection


playerAddedConnection = game.Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		
		for i , child in pairs(folder:GetChildren()) do

			local newmob = mobmod.New(child , i)
			print(i)

			runConnection =	game:GetService("RunService").Heartbeat:Connect(function(dt)
				
				if not character:WaitForChild("HumanoidRootPart") then
					error("attempt nil on HumanoidRootPart")
					--runConnection:Disconnect()
				
				
				elseif (character:WaitForChild("HumanoidRootPart").Position - child.HumanoidRootPart.Position).Magnitude < 15 then
					mobmod:Chase(child , character:WaitForChild("HumanoidRootPart"))
				end
				
				mobmod:Attack(child , character , 20)
				
			end)

			childConnection = child.Humanoid.Died:Connect(function()

				mobmod:Dead(child , runConnection)	
				
			end)
		end
	end)
end)

folder.ChildRemoved:Connect(function(child)
	
	childConnection:Disconnect()
	runConnection:Disconnect()
	playerAddedConnection:Disconnect()
	
	print(runConnection.Connected , childConnection.Connected , playerAddedConnection.Connected)
	
	for i , child in pairs(folder:GetChildren()) do
		local newmobs = mobmod.New(child , folder)
	end
	
end)

pls let me know if you need anything

From what I can understand, one of these module’s functions have a Touched event and since events run parallel and it’s in an event, that error gets thrown. Correct me if I’m wrong please.

So whats ur saying is that I ran an event that is inside another event that causing this error??

No, some events are not safe to run in parallel, it’ll say so in their DevHub page:

https://developer.roblox.com/en-us/api-reference/event/BasePart/Touched

It’s not the same for all events.

Can you tell me what is parallel is actually . I cant find any in devhub

I’m not an expert on it but from what I know, basically all other threads running on the same runtime is being called running in parallel. As you know, event calls run on a separate thread than your main script.

So you mean that there is multiple event running on the same time or something??

1 Like

You should know these for now:

  • Events run on another thread (check what threads are if you don’t know)
  • Some events are not safe to access in other threads from a script and you shouldn’t use them in threads.

Most events are safe to run in threads, however it’ll throw this error if it’s not. Their Developer Hub page tells about it too. You should use Developer Hub if you aren’t using it.

So from what my understanding is

thread
Like doing 2 task at same time

And from what I found in DevHub is that delay() allow you to run multiple thread in same parallel. Pls do correct me if Im wrong

delay() is only one method of creating threads. There are really good articles about these in DevForum. I suggest reading them.

This error means you’re desynchronizing your thread somewhere either with task.desynchronize or RBXScriptSignal:ConnectParallel, both of which are products of Parallel Lua Beta and try to index BasePart.Touched event with it from a desynchronized thread.

While functions like RBXScriptSignal:Connect, task.delay/spawn/defer, coroutine.create/wrap create separate threads, they are not actually desynchronized threads so those functions are not the problem here.

can you explain a bit more easier . sry , having hard time to understandin it :smile:

Alright then let’s go a bit more on the basic level.

So let’s talk about task.spawn/defer/delay, coroutine.create/wrap and spawn/delay functions. While all these functions behave differently, they all have one purpose in common: To run multiple parts of code seamlessly at the same time via using separate thread objects. You might think of separate coroutines/threads (Whatever you want to call them.) as separate physical cores of processor (CPU on your computer.) doing tasks at the same time but that’s not exactly the case.

You see, while the Roblox engine uses actual multithreading for physics simulations, rendering, ect., the language and the language that the former language is based on that you write your code in for the game, Luau and vanilla Lua 5.1.4, always used “asymmetric coroutines” at the core, always has been. This means that all those threads actually runs on a single core on your CPU. What asymmetric coroutines does to mimic “multithreading” it that it switches between different threads very quickly by yielding the thread it’s currently executing and unyielding another thread and start executing that one and so on. That means every script in your game runs on a single physical core.

Now while this is convenient since you won’t run into problems that you normally would if you were writing your code in a language that supports actual multithreading, this becomes a bit problematic when you try to do more complex stuff like real-time terrain generation, doing real-time raytracing, mining bitcoins on a poor 8 year old’s 6 year old PC (Please don’t do cryptocurrency mining on someone else’s computer please. This is just a terrible joke.), ect. as that takes too many computing power that developer doesn’t have in a specific timeframe. Which is where Roblox’s Parallel Lua Beta and desynchronized threads comes in.

Now to explain how Roblox’s multithreading works, I need to explain you how Luau works a bit on core level. Now in Luau, the code is compiled into bytecode first then that bytecode is run on a Luau process virtual machine (Before you ask, no, it’s not the same thing as operating system virtual machines.). Now because of how Luau Virtual Machine is designed, they can’t really do anything that’s actually multithreaded.

The way Roblox got around this problem is that they implemented an Actor Model with Actor instances, which is in short terms, you separate your place into different chunks of Actor instances that gives your code ability to run on desynchronize threads at request (Which is possible with task.desynchronize or RBXScriptSignal:ConnectParallel for events.) by using separate Luau Virtual Machines that runs in separate physical cores in your CPU, as long as you’re not trying to access anything else that is outside of that Actor instance while in desynchronized mode, that is (You can get out of desynchronized mode by using task.synchronize).

Of course while this is cool and all, this creates some restrictions for a sizeable amount of properties and functions of instances because of how multithreading works as 2 separate threads that are not synchronized in relation to each other might mutate the instance hierarchy while ignoring each other and might create unwanted results. These properties are marked as Unsafe to be used by unsynchronized threads which is where our issue arises.

Now, I don’t know why accessing Touched and TouchEnded events might cause problems (Maybe they forgot or there is some technical reason behind it but I’m not a Roblox engineer that worked on the beta so what do I know?) but you somehow, somewhere in your code try to access BasePart.Touched event in a desynchronized thread and that is causing some issues, so you need to search for either task.desynchronize or :ConnectParallel functions in your code and modules and synchronize the thread, connect the event then desynchronize the thread or get rid of Actor implementation altogether from your code.

8 Likes

This actually seems to be a bug, on which for me sometimes, for a while, I have seen stuff just go in parallel for no apparent reason.

For me it only ever happened on the command bar and from plugins, Rojo errored multiple times for me from this.

It seems pretty random and somehow rare, I think it might have to do something with task.spawn/defer, but not sure, the time I got this error for the first time, I was using task.spawn, so…?

image

Reclass hasn’t been updated since April, task’s new functions were not available back then and I certainly haven’t had this issue before.

There’s definitely something going on with the task scheduler or something for it to be putting normal events / resuming into parallel for no reason.

I’ll be looking into this further, but for now… Could not find any pattern about it, especially giving I’m usually getting this error from other people’s plugins. If I find anything I’ll be reporting it.

Also for anyone having this error with Rojo pretty frequently, (which bricks it) I recommend turning off auto save and just saving with Ctrl + S anytime you are finished writing some code.

3 Likes

It seems to me like only a few dozen people know how this whole parallel Luau thing even works. I can barely find any explanations on it. Here’s a late thank you for providing your wisdom

I still don’t totally understand what this means. Mutating the instance hierarchy might create what unwanted results? One desynchronized thread finishing first will create a different result than if another thread finished first, i.e a race condition? That seems to me like it would only happen if you were trying to have two threads modify the same thing, and would be purely a bad design choice on the developer’s part.

I want to split up many separate RenderStepped tasks into different threads. They’re all supposed to run BulkMoveTo on each of their separate sets of rotating objects scattered around the map, whether it’s coins that rotate and bob up and down (1 thread), or signs around the map that slowly spin when in the camera’s view (another thread), it just makes sense that these performance heavy operations could be split up into separate threads. But BulkMoveTo is apparently an unsafe operation, and I can’t find any information about why or anyone else who experienced the same issue.