Threading - forcing to wait till the change of variable to true.

Hello.
I am making a game in which a player has to write a code in order to move the tank (shoot, drive, etc.).
I am using Roslyn Compiler to run an Interpreter (How to Write Your Own Programming Language in C#) which interprets the code written by player and run functions written in Unity. I am facing a problem where i need the interpreter to wait before continuing its execution.
For example:
I have a function “ruch(number)” which stands for “move(number)” and it calls prepared in unity function which moves a tank based on the number (0-5 => which represents NE, E, SE, SW, W, NW). So I write the script
ruch(1); and run it with CSharpScript.RunAsync(code, ScriptOptions.Default.WithImports("System", "UnityEngine", "SplitAndMerge").AddReferences(typeof(UnityEngine.Transform).Assembly, typeof(SplitAndMerge.Program).Assembly));
Then the interpreter “plays” ruch(1); method which is implemented:

protected override Variable EvaluateAsync(string data, ref int from)
        {
            //Variable is defined as static in the class where this method is placed in.
            ifMovementFinished = false;
           
            Debug.LogWarning("Interpreter Thread: " + Thread.CurrentThread.ManagedThreadId);
         
            Variable arg1 = Parser.LoadAndCalculate(data, ref from, Constants.END_ARG_ARRAY);

            GameObject gameOb = GameObject.FindWithTag("PlayersScriptHandler");

            Dictionary<int, bool> param = new Dictionary<int, bool>();
            param.Add((int)arg1.Value, false);
            gameOb.SendMessage("makeMovementAsync", param);

            if (!param[0])
            {
                Debug.LogError("You can not go through the wall.");
            }
            else
            {
//Here it should be waiting for the ifMovementFinished variable to become true;
            }
            ifMovementFinished = false;
            return Variable.EmptyInstance;
        }

As you can see this function calls makeMovementAsync on a gameobject with tag. It is implemented:

 public void makeMovementAsync(Dictionary<int, bool> param)
    {
        Debug.LogWarning("Main Thread: " + Thread.CurrentThread.ManagedThreadId);

        param[0] = GameObject.Find("GameUI").GetComponent<HexGameUI>().setCurrentCell(param.FirstOrDefault(entry => EqualityComparer<bool>.Default.Equals(entry.Value, false)).Key);
        if (param[0]) // it means that setCurrentCell has determined the tank can move to this place and it started moving it;
        {                   
            StartCoroutine(CR_GetMove());
        }
       
    }

 IEnumerator CR_GetMove()
    {
        Thread thread = new Thread(new ThreadStart(async () =>
        {
            Debug.LogWarning("Thread waiting for movFinish: " + Thread.CurrentThread.ManagedThreadId);
            while (!checkIfMovementFinished())
            {
                await Task.Delay(1);
            }
            UnRuch.setMovementFinished(true); // it calls Interpreter's function to change ifMovementFinished to true;
        }));
        thread.IsBackground = true;
        thread.Start();

        while (thread.IsAlive) yield return null;

        yield break;
    }

I need it to wait till the bool to come true, because it is when the tank “stops” moving. Not waiting for it to finish causes that if my script was ruch(1); ruch(1); there would be an effect like only one instruction was called because when the second one would be in “progress” the position of tank still wouldn’t be changed by first instruction. Same would happen if i wrote ruch(1); shoot(2); ~ in the future.
As you can see i really need to wait for the change of bool variable. I tried many things - semaphores, creating new threads, but each time it was a failure - it either didn’t wait for the change or i got deadlock - since both main unity thread and interpreter ran by CSharpScript.RunAsync() works on the same thread. Trying to run it on seperate one causes that interpreter can’t find objects from the game. Moreover i can’t try await instruction in interpreter’s function because it is overriding a method and I can’t change method signature.
You guys are my last hope to get that work. :slight_smile:

Edit:
Simply (I really doubt it is possible) I’d like to tell the thread of interpreter and unity that he shouldn’t proceed with interpreter unless the corutine has set the bool to true.

becouse your interpreter works in main thread but you need to wait to en of some action that take multiple frames you need to make something async.
I see few options:

  1. change EvaluateAsync to async. but it will work only if you could change base class.
  2. move interpreter to separate thread but use some kind of dispatch to not call unity methods from that thread.

I can’t change EvaluateAsync to async because async methods cant have ref parameters. :confused:
So I have to choose option 2, right? Is it even possible though? I’d need to tell THREAD_1 (Unity) from THREAD_2 to run function on THREAD_1. :confused:

About 1. if you really could change EvaluateAsync signature you could change it to not use ref. Like this: protected override Task<(Variable Variable, int From)> EvaluateAsync(string data, int from)
About 2. Yes, you can.
Simplest implementation I could think about

public class DispatchHelper
{
    private static SynchronizationContext _mainThreadContext;
// RuntimeInitializeOnLoadMethod always runs on main thread and unity main thread always have SynchronizationContext
    [RuntimeInitializeOnLoadMethod]
    private static void Init() => _mainThreadContext = SynchronizationContext.Current;

// Send wait while action will finish it work on main thread
    public static void Dispatch(Action action) => _mainThreadContext.Send(s => ((Action) s)(), action);
}

...............
        protected override Variable EvaluateAsync(string data, ref int from)
        {
            //Variable is defined as static in the class where this method is placed in.
            ifMovementFinished = false;
        
            Debug.LogWarning("Interpreter Thread: " + Thread.CurrentThread.ManagedThreadId);
      
            Variable arg1 = Parser.LoadAndCalculate(data, ref from, Constants.END_ARG_ARRAY);

            Dictionary<int, bool> param = new Dictionary<int, bool>();
            param.Add((int)arg1.Value, false);

           DispatchHelper.Dispatch(() =>
           {
                GameObject gameOb = GameObject.FindWithTag("PlayersScriptHandler");
                gameOb.SendMessage("makeMovementAsync", param);
            });
         
            if (!param[0])
            {
                Debug.LogError("You can not go through the wall.");
            }
            else
            {
                // wait
            } 
            ifMovementFinished = false;
            return Variable.EmptyInstance;
        }

I really appreciate your post, but i will try it tomorrow. My mind is exploding after “fighting” with it for several hours. ^^

Instead of a bool, use a WaitHandle:

I personally would specifically use a AutoResetEvent (inherits from EventWaitHandle->WaitHandle):

Instead of ifMovementFinished being a boolean. Make it a AutoResetEvent:

private AutoResetEvent ifMovementFinished = new AutoResetEvent(false);

Then your code would be like:

        protected override Variable EvaluateAsync(string data, ref int from)
        {
            ifMovementFinished.Reset(); //reset event to make sure it'll block when you call WaitOne

            Debug.LogWarning("Interpreter Thread: " + Thread.CurrentThread.ManagedThreadId);

            Variable arg1 = Parser.LoadAndCalculate(data, ref from, Constants.END_ARG_ARRAY);

            GameObject gameOb = GameObject.FindWithTag("PlayersScriptHandler");

            Dictionary<int, bool> param = new Dictionary<int, bool>();
            param.Add((int)arg1.Value, false);
            gameOb.SendMessage("makeMovementAsync", param);

            if (!param[0])
            {
                Debug.LogError("You can not go through the wall.");
            }
            else
            {
                ifMovementFinished.WaitOne();
            }
            return Variable.EmptyInstance;
        }

And your setMovementFinished should actually call the ‘Set’ method of the wait handle:

private void setMovementFinished()
{
    ifMovementFinished.Set();
}

Note that this assume EvaluateAsync is NOT on the main thread. Otherwise WaitOne will block the main thread. I’m guessing it is its own thread because of your LogWarning there checking the thread id. But I don’t see where in your code you actually spin up threads… so you need to make sure you work that into your logic.

If you go this route and your program locks up in a deadlock… well we’ll need to see all your involved code for spinning up threads and how you deal with threading in general.

It was/is running on the same thread indeed. You didn’t get my point. I used Debug.LogErrors to see whether the threads that unity and interpreter run were the same.

If you’re on the same thread you’re going to have to break out into more than just this function. There’s no line of code you can put in that else to wait.

  1. start using threads, tasks, some asynchronous helper

  2. start using coroutine to wait and yield until ifMovementFinished is true

  3. have an Update loop that waits.

From your current code a Coroutine would be the easiest to refactor into. Just change EvaluateAsync into a coroutine (or make it start up a coroutine). And then in that else statement just put:

while(!ifMovementFinished) yield return null;
  1. I couldn’t use threads since both unity and interpreter ran on the same thread because I didn’t know how can I make “outer thread” call Unity functions (the way VolodymyrBS suggested).
  2. Can’t start coroutine in outer .dll
  3. Its outer .dll so “Update()” wouldn’t work ~I think so ^^

I couldn’t wait to check this solution and adding while(!ifMovementFinished)Thread.Sleep(50); made my code work as i intended it to be. I really appreciate your help! You’re my savior :smile:

Well, you can’t block mid method with out some sort of asynchronicity going on (think like the async/task library, or threads).

Unity hacks a way around this with Coroutines exploiting iterator functions to simulate asynchronous behaviour via the yield statement.

You’ll need to fashion something along these lines in your dll. Easiest will likely be the Task library with a synchronization context to the unity thread. Again, since I don’t know the design of your code this isn’t some straight forward thing. What you’ll need to do though is have an entry point in your dll to start this whole process and hand it said synchronization context… then this method in question, EvaluateAsync, can await the coroutine at this point.

This is almost what VolodymyrBS does… but from the look of their code it won’t wait for ifMovementFinished to be true, it just sends the message. There’s nothing in their that waits until your coroutine in makeMovementAsync finishes. You’d need to add logic to his method (where he comments you don’t need to wait) to await ifMovementFinished.

Alternatively if you don’t like async/task stuff, you could alternatively in the entry point of your dll pass your code a GameObject it can hook into for coroutines/update.

[edit] It looks like while I was typing you got VolodymyrBS’s solution to work using a Thread.Sleep… though I will point out that this implies you’re on a different thread. The fact you’re not aware of that, combined with how async/tasks works, you may want to be careful. This could potentially cause a deadlock. Alternatively you could use SemaphoreSlim.WaitAsync similar to WaitHandle.WaitOne.

I am and was aware about “thread situation”. At first when i created a post i was having both unity and interpreter ran on same thread => which i checked by printing ids. Later on when VolodymyrBS posted I did change interpreter to be run on new thread and therefore i could use Sleep() on it :wink: