CompareBaseObjectsInternal can only be called from the main thread.
Constructors and field initializers will be executed from the loading thread when loading a scene.
Don’t use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.
I’m receiving this error, but I can’t find the exact reason.
The strange thing is that it happens only from the second run of the application inside the editor. If I restart Unity only for the first run there is no error.
With a suggestion on what could cause it, I should be able to find the offensive code.
after deep testing I found that the problem arise inside a destructor of a class NOT inherited from MonoBehaviour.
It was just one line of code to create the problem:
if (_pool != null) {
where _pool is a class pointer of type SpawnPool.
The strange thing is that the destructor gets called when I restart the game from the editor, and not when I stop it.
The only workaround I found is to move this code in a Dispose() method.
Do you think that is a Unity bug to be reported or am I misunderstanding something?
In mono and .NET destructors is called by the garbage collector working in a seperate thread. The same happens with constructors on scene load. Dispose is a bit more tricky, if nobody have called Dispose then the garbage collector will call dispose, but if the object have been properly disposed it will not. So if you remove an object from the Unity scene hierarchy its really up to you to ensure that Dispose gets called from main-thread. As long as you don’t detach the object from the scene it gets disposed correctly when you load a new scene.
So if you add this code to a MonoBehaviour and put it in a scene you’ll get similar errors.
public Texture2D internalTexture = new Texture2D(256, 256);
Unity chose to limit API calls to main-thread, to make a simple and solid threading model that everyone can understand and use. This includes simple object comparison, and apperantly SpawnPool inherits from UnityEngine.Object rather than directly from System.Object.
Thank you very much for your explanation. Very useful and comprehensive.
Yes I confirm you that SpanPool inherits from MonoBehaviour so that’s the reason of the error.
I’ve just run into this same issue today. I understand what’s going on, but it seems to me that it raises two important questions I haven’t found good answers for:
How can our code tell whether it’s currently running on the main thread? Clearly Unity code can tell, as it raises this error. I suppose one method would be to try to access some Unity function, and trap the exception, but that’s hardly elegant… is there a “IsMainThread” method somewhere?
Is there any simple method to invoke a method on the main thread?
For what it’s worth, both of these tasks are pretty easy in Cocoa, which has similar limitations about calling most framework functions from other threads. (See NSObject’s performSelectorOnMainThread:withObject:waitUntilDone: method for example.) Surely Unity provides something similar?
Sorry to reply to my own post, but this seemingly simple need (how to tell if we’re on the main thread) is even harder than I thought.
Even with a try/catch, the Unity error appears in the console and (if you have Error Pause on) pauses your game. That makes it pretty much unusable for me — I’m not willing to turn Error Pause off.
And the standard C# recommendation of using Thread.CurrentThread.ManagedThreadId does not appear to work. I get a value of 1 both on the main thread, where Unity APIs work, and on a second thread, where they fail.
So the question remains open… how can we tell if we’re on the main thread, so we know whether it’s safe to call Unity APIs?
Not sure if anybody is still struggling with this issue, but this was my experience:
Basically I am using .NET sockets and I have an empty GameObject with a script named “Connector” that holds all the networking code. In one of the ASync receive functions, I try to access the static variable named Instance on that game object in order to set a string message variable to something (and since in onGui() it checks for something in that message, it can display some result).
Anywho, the fact is that the async receive function is managed (unless I’m mistaken) on a separate thread and for some reason Unity doesn’t like when the main thread components are used in ‘if’ statements…
It throws the error you describe if I execute:
if (Connector.Instance != null)
In which I am trying to execute
Connector.Instance.message = “Message received”;
Interestingly enough, it lets me set this message variable just fine in spite of the fact that it can’t do that comparison (i.e. if I don’t do the if statement and just set the message).
Kind of odd, that’s for sure. I would think if there is some sort of scary non-thread safe stuff going on there, that surely my setting that string variable is much more dangerous than doing a comparison with something not on that thread.
I think it’s because != is invoking the comparison method on the object, which is a Unity function, and so does the thread check. If you used Object.ReferenceEquals instead, it’d probably be fine.
But, for the record, I still find it astounding (and darned inconvenient) that there is no way to tell when you’re on the main thread.
I have another question about this… how do we locate these errors? They showed up in the middle of a hectic time in the project, but since they didn’t seem to stop anything from working, we didn’t try to fix it.
Now I’m going back to fix it, and I don’t get any information about where the error is occurring.
If you’re just checking references, use “ReferenceEquals(obj, null)”, since as the name suggest it only checks the reference and is thus thread safe. This will avoid Unity’s error.