3

I have a problem trying to manipulate GameObjects inside the ContinueWith method of a Task instance. The code below worked pretty well until we switched to .NET 4.x in Unity 2018.2.

I've searched a lot and tought I found what it seems to be a workaround (using UnityThread), I could't find the reason why this code doesn't work anymore and since the same pattern is used a lot in my project I want to know if there is in fact no other solution.

FirebaseDatabaseManager.Instance.GetPrivateChatMessages(chatId)
    .ContinueWith(messagesTask =>
    {
        if (messagesTask.IsCompleted && messagesTask.Result != null && messagesTask.Result.Exists)
        {
            // Go over the dictionary in reverse.
            foreach (var message in messagesTask.Result.Children.OrderByDescending(x => x.Key))
            {
                Debug.Log("Message " + message.Key);

                // Insert the message as the first element.
                StartCoroutine(AddMessageItem(message));
            }
        }
    });

EDIT: This is the method GetPrivateChatMessages.

/// <summary>
/// Returns the private chats' messages or null if the database is not initialized.
/// </summary>
/// <returns></returns>
public Task<DataSnapshot> GetPrivateChatMessages(string chatId, int limit, string lastItemKey, long lastItemTimestamp)
{
    if (!Initialized) return null;

    return DatabaseRoot.Child("messages").Child(chatId).OrderByChild("timestamp").EndAt(lastItemTimestamp, lastItemKey).LimitToLast(limit).GetValueAsync();
}

Before the update the line StartCoroutine(AddMessageItem(message)); used to work just fine, now it throws an exception

01-08 13:47:15.376 23882-24991/com.fhacktions E/Unity:
IsObjectMonoBehaviour 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.
  UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
  Fhacktions.c__AnonStorey2:<>m__0(Task`1)
  System.Threading.Tasks.Task:Execute()
  System.Threading.ExecutionContext:RunInternal(ExecutionContext, ContextCallback, Object, Boolean)
  System.Threading.ExecutionContext:Run(ExecutionContext, ContextCallback, Object, Boolean)
  System.Threading.Tasks.Task:ExecuteWithThreadLocal(Task&)
  System.Threading.Tasks.Task:ExecuteEntry(Boolean)
  System.Threading.ThreadPoolWorkQueue:Dispatch()
  
  [/Users/builduser/buildslave/unity/build/Runtime/Scripting/ScriptingThreadAndSerializationSafeCheck.cpp line 85]
  (Filename: /Users/builduser/buildslave/unity/build/Runtime/Scripting/ScriptingThreadAndSerializationSafeCheck.cpp Li

EDIT: Passing TaskScheduler.FromCurrentSynchronizationContext() as a parameter to the ContinueWith method solved the problem, I guess the default implementation changed and that caused the problem after the switch. Thanks.

derHugo
  • 83,094
  • 9
  • 75
  • 115
Votagus
  • 544
  • 6
  • 9
  • 1
    Is `GetPrivateChatMessage` something you wrote? If so please show it – derHugo Jan 08 '19 at 18:30
  • 1
    Possible duplicate of [Task continuation on UI thread](https://stackoverflow.com/questions/4331262/task-continuation-on-ui-thread) – derHugo Jan 08 '19 at 18:35
  • 1
    @derHugo I edited the question to add the method, thanks and yes, the answer in that question pretty much solves the problem, my only guess is that the default implementation changed at some point between the .NET versions. – Votagus Jan 08 '19 at 19:10

0 Answers0