I use Firebase with Unity and I get errors like this ( in Visual Studio – Unity doesn’t throw any errors at all about this… ) when Firebase Database callbacks that all data is ready:
someTransfrom.position = UnityEngine.UnityException: get_position 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 mo…
It also happens for UI and other stuff… -however it worked in the past. Same code… only older ( final ) Unity version and older Firebase version.
I remember this from the past in other alphas/betas… -wonder why this happens…
Then I // Do something with snapshot… like to set an objects position or an uGUI element: I get the error mentioned above. BUT like I wrote… it worked fine before the Unity and Firebase update.
I made an Unity and Firebase update in September 2018. It worked in Unity 2018.2.6 and Firebase 5.2.1.
Now I use Unity 2019.1b1 and Firebase 5.4.4. with .Net 4.x. and it’s broken.
Btw… the probably easiest way to get the error is to use this snippet within the Lamda expression:
Does it work if you revert to Firebase 5.2.1 or if you try Firebase 5.4.4 with Unity 2018.2.6? I don’t think anything changed with threading on Unity side. If you think it did, please report a bug.
In either case, you’ll have to call the APIs you want from the right thread.
I have had this before. Firebase’s implementation for Mono used to run its callbacks on the main thread. The one for .NET 4.x does not. It can be fixed by passing System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext() as the second argument to ContinueWith.
It is actually better to have callbacks in a thread, that way you can do stuff that is thread safe, and then call to your main thread only when you need to.
I do something like:
ContinueWith( task => {
// do what you can here in an alternate thread
..
// based on the results, we need to do something on the Unity Thread:
SyncContext.RunOnUnityThread( () => {
// Do Unity stuff
});
});
An example of SyncContext might be:
public class SyncContext : Monobehaviour
{
public static TaskScheduler unityTaskScheduler;
public static int unityThread;
public static SynchronizationContext unitySynchronizationContext;
static public Queue<Action> runInUpdate= new Queue<Action>();
public void Awake()
{
unitySynchronizationContext = SynchronizationContext.Current;
unityThread = Thread.CurrentThread.ManagedThreadId;
unityTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
public static bool isOnUnityThread => unityThread == Thread.CurrentThread.ManagedThreadId;
public static void RunOnUnityThread(Action action)
{
// is this right?
if (unityThread ==Thread.CurrentThread.ManagedThreadId)
{
action();
}
else
{
lock(runInUpdate)
{
runInUpdate.Enqueue(action);
}
}
}
private void Update()
{
while(runInUpdate.Count > 0)
{
Action action = null;
lock(runInUpdate)
{
if(runInUpdate.Count > 0)
action = runInUpdate.Dequeue();
}
action?.Invoke();
}
}
Thanks for the code. A question, what are you using unitySynchronizationContext and unityTaskScheduler for. I see they are not bing used in this class, so i assume the class is probably simplified, is it?
I just a similar problem when upgrading from Unity .net 3.5 to 4.x and Firebase 4. This problem seems to happen for UI related elements. You can add TaskScheduler.FromCurrentSynchronizationContext()as an argument for your ContinueWith function to fix the issue.
Example:
FirebaseDatabase.DefaultInstance
.GetReference("Leaders")
.GetValueAsync().ContinueWith(task => {
if (task.IsFaulted) {
// Handle the error...
}
else if (task.IsCompleted) {
DataSnapshot snapshot = task.Result;
// Do something with snapshot...
}
} ,TaskScheduler.FromCurrentSynchronizationContext() );