Data stored in Roblox global `shared` seems to cross actor boundaries causing race conditions

Problem

It appears that the Roblox global shared is not isolated to an individual Actor for thread safety when using the global variable array in Parallel LUA. It seems to be shared across all LUA VMs similar to a SharedTable in contradiction to the documentation. I have tested this in the live server, and it’s not an issue. It only happens when play testing in Studio. This happens in both solo play mode and local server mode. I converted my codebase to a slightly different format to reduce the size of my workflow. I have been using this setup for about a week or two with no problems. It seems that after the last update, it suddenly broke.

My understanding (and this was confirmed by a Roblox engineer) that each actor represents a separate LUA VM. Since the behavior of shared seems to have changed, this is now causing a race condition within my code. I have to run the play test multiple times before I can get an instance with no errors and I can actually test stuff.

The live game can be found here: https://www.roblox.com/games/15112688736/The-Proving-Grounds

The repo file is a vast simplification of the system that I am using. I have designated each actor as a package which contains various module scripts, assets, etc…, but only one actual main script. That script is responsible for establishing function binding and BindableFunction invoke targets as well as loading module scripts within the actor. It is also responsible for establishing the actor wide global packageName that is used by those module scripts for initialization. Based on the current documentation as well as the aforementioned Q&A session with a Roblox engineer, and other forum posts, each actor has it’s own unique packageName global, or at least they are supposed to.

Additional Information

The beta features that I have enabled are as follows:

  • Notched Screen Support
  • Notification Overhaul
  • Updated Docking System

The Updated Docking System was turned on recently right before this broke. I have disabled this beta feature and the problem is still occurring. I have also disabled all 3rd party plugins and the issue still occurs.

Due to the nature and severity of the issue, this probably should be looked at sooner rather than later.

Field Value
Problem Area Studio
Problem Component Play Testing (Solo and Local Test Server)
First Noticed 15 Dec 2023
Frequency Nearly Constant
Impact Major (Development Progress Impacting)

Expectations

What I expect to happen is that global variables are isolated within their own actors.


Visuals

The below errors should not be happening if shared was not being shared across actors.


As seen from the image, the assert fails because the packageName global and the actor that the script is running under is different, even though the main script sets packageName to the name of the encompassing actor.


Reproduction

  1. Load the reproduction file (below) into Studio.
  2. Start play testing.
  3. Observe results.

Note: May need to playtest multiple times for the errors to show up, but usually not.


Files

Studio - Actor Global Variables.rbxl (62.1 KB)

A private message is associated with this bug report

3 Likes

This is not correct, in the original answer you can see the note:

Because of that, shared global table (and _G as well) can be shared between different actors when they are assigned to the same Luau VM.
It’s still thread-safe in a sense that CPU hardware threads will not interfere.

1 Like

Ok. That was missed. What is the criteria that causes actors to share VMs? Is there a way to force each actor to have it’s own Luau VM? Or is that something that’s determined internally by the engine? A lot of this really isn’t documented. There’s no mention of it in the documentation of _G and shared.

The number of VMs is limited and depends on the number CPU cores.

Yes, it’s determined by the engine and is not possible to control.

Fair enough. Generally speaking, would it be correct to assume that the number of VMs is the same as the number of CPU cores plus one additional for the main thread? I realize that this is more of an implementation question but I’m curious. Also, is there a way to identify which VM a script is executing in?

In the interim, I have corrected the issue on my end my implementing a mutex system to protect the global variable during the critical parts of package initialization. So at this point, the issue is solved.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.