Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Coroutine gets stuck at yield statement

Discussion in 'Scripting' started by Pincellox, Aug 22, 2022.

  1. Pincellox

    Pincellox

    Joined:
    Dec 21, 2018
    Posts:
    5
    I am trying to send a post request to an API. I made a coroutine with a "yield return request.SendWebRequest()" statement and a callback function gets executed afterwards.

    Code (CSharp):
    1. IEnumerator Post(string url, string requestString, Action<string> callback)
    2.         {
    3.             var request = new UnityWebRequest(url, "POST");
    4.             byte[] bodyRaw = Encoding.UTF8.GetBytes(requestString);
    5.             request.uploadHandler = new UploadHandlerRaw(bodyRaw);
    6.             request.downloadHandler = new DownloadHandlerBuffer();
    7.             request.SetRequestHeader("Content-Type", "application/json");
    8.             Debug.Log("before");
    9.             yield return request.SendWebRequest();
    10.             Debug.Log("after");
    11.             callback(request.downloadHandler.text);
    12.         }
    However, the debug log with "before" in it gets executed. The debug log with "after" in it is never executed, nor is the callback function and Unity freezes. No errors are thrown in the console.
    If I comment out the callback function, the debug log with "after" in it works and Unity does not freeze. I used the content of the callback function outside of this coroutine and it works fine.

    I have no idea why this happens and I did not find any other threads with a solution.
    Some help is appreciated. Thank you! :)
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,735
    Can you show the callback function? It seems highly likely that there is an infinite loop in that function. That would explain all of the behavior you are seeing.

    You said you tried out outside this coroutine, sure, but it may be specifically that the data returned by this coroutine causes that function to enter an infinite loop. Please share the callback code.

    Also try simply printing out the downloaded data and not running the callback. See what happens then.
     
    Bunny83 likes this.
  3. Pincellox

    Pincellox

    Joined:
    Dec 21, 2018
    Posts:
    5
    Thank you for replying.

    This is the callback function. It's really big chunk of code, sorry.

    Code (CSharp):
    1. if (!string.IsNullOrEmpty(result))
    2.                 {
    3.                     MapquestJsonObject obj = JsonConvert.DeserializeObject<MapquestJsonObject>(result);
    4.                     for (int i = 0; i < obj.locations.Count; i++)
    5.                     {
    6.                         Vector2 latLng = new Vector2((float)obj.locations[i].latLng.lng, (float)obj.locations[i].latLng.lat);
    7.                         fullAddressSet.Add(new Address(i, "", latLng, i == 0 ? 0 : UnityEngine.Random.Range(1800f, 7200f)));
    8.                     }
    9.  
    10.                     costMatrix = obj.time;
    11.  
    12.                     if (debugMode)
    13.                     {
    14.                         Debug.Log("fullAddressSet: " + AddressListToString(fullAddressSet));
    15.                         foreach (Address item in fullAddressSet)
    16.                         {
    17.                             Debug.Log(item.ToString());
    18.                         }
    19.  
    20.                         string str = "";
    21.                         foreach (var item in costMatrix)
    22.                         {
    23.                             str += "[";
    24.                             foreach (var item2 in item)
    25.                             {
    26.                                 str += item2 + " ";
    27.                             }
    28.                             str += "]\n";
    29.                         }
    30.                         Debug.Log("costmatrix:\n" + str);
    31.                     }
    32.  
    33.                     List<Address> fullSetWithoutStart = new List<Address>(fullAddressSet);
    34.                     fullSetWithoutStart.RemoveAt(0);
    35.  
    36.                     double lowestCost = Mathf.Infinity;
    37.                     int highestAddressAmount = 0;
    38.                     List<Tour> finalDays = new List<Tour>();
    39.                     int bestSeed = -1;
    40.  
    41.                     for (int i = 0; i < seedAmount; i++)
    42.                     {
    43.                         if (debugMode) Debug.Log("-------------- seed " + i + " --------------");
    44.  
    45.                         List<Vector2> latLngList = fullSetWithoutStart.Select(o => o.location).ToList();
    46.                         int[][] clusters = KMeans.Cluster(latLngList.ToArray(), daysAmount, 50, i).clusters;
    47.  
    48.                         double cost = 0;
    49.                         int addressAmount = 0;
    50.                         List<Tour> tmp = new List<Tour>();
    51.  
    52.                         foreach (int[] cluster in clusters)
    53.                         {
    54.                             if (debugMode) Debug.Log("----------- cluster " + Array.IndexOf(clusters, cluster) + " -----------");
    55.  
    56.                             List<Address> addressCluster = cluster.Select(o => fullSetWithoutStart[o]).ToList();
    57.  
    58.                             float start = Time.realtimeSinceStartup;
    59.                             Tour bestTour = GetBestTour(addressCluster);
    60.                             float end = Time.realtimeSinceStartup;
    61.  
    62.                             bestTour = new Tour(bestTour.addressList.Prepend(fullAddressSet[0]).ToList());
    63.  
    64.                             Tour clampedTour = GetClampedTour(bestTour, maxDayCost);
    65.  
    66.                             if (debugMode)
    67.                             {
    68.                                 Debug.Log("execution time: " + (end - start).ToString("F2") + " seconds");
    69.                                 Debug.Log("bestTour: " + AddressListToString(bestTour.addressList) + ", cost: " + bestTour.cost);
    70.                                 Debug.Log("clampedTour: " + AddressListToString(clampedTour.addressList) + ", cost: " + clampedTour.cost);
    71.                             }
    72.  
    73.                             addressAmount += clampedTour.addressList.Count;
    74.                             cost += clampedTour.cost;
    75.                             tmp.Add(clampedTour);
    76.                         }
    77.  
    78.                         if (addressAmount < highestAddressAmount)
    79.                         {
    80.                             continue;
    81.                         }
    82.                         else if (addressAmount == highestAddressAmount)
    83.                         {
    84.                             if (cost >= lowestCost)
    85.                             {
    86.                                 continue;
    87.                             }
    88.                         }
    89.  
    90.                         if (debugMode) Debug.Log("seed " + i + " is current best\naddressAmount: " + addressAmount + "\ncost: " + cost);
    91.  
    92.                         highestAddressAmount = addressAmount;
    93.                         lowestCost = cost;
    94.                         bestSeed = i;
    95.                         finalDays = tmp;
    96.                     }
    97.  
    98.                     string temp = "";
    99.                     foreach (var item in finalDays)
    100.                     {
    101.                         temp += AddressListToString(item.addressList) + "\n";
    102.                     }
    103.                     Debug.Log("~~~~~~ best days ~~~~~~\nseed: " + bestSeed + "\n\n" + temp + "\nAddress Amount: " + highestAddressAmount + "\nCost: " + lowestCost + "\n");
    104.                 }
    105.                 else
    106.                 {
    107.                     Debug.Log("Post request returned empty response");
    108.                 }
    The only while-loop there is, is the following. It is inside the function "GetBestTour" used in the code above on line 59.

    Code (CSharp):
    1.  
    2. public Tour GetBestTour(List<Address> addressList)
    3.         {
    4.             float timeStamp = Time.realtimeSinceStartup;
    5.             r = new Random();
    6.  
    7.             Tour dest = new Tour(addressList);
    8.             Population p = Population.randomized(dest, GlobalVariables.popSize);
    9.  
    10.             int gen = 0;
    11.             bool better = true;
    12.             while (gen < GlobalVariables.maxIterations)
    13.             {
    14.                 if (better && debugMode) display(p, gen);
    15.  
    16.                 if (Time.realtimeSinceStartup - timeStamp > maxExecutionTime / (daysAmount * seedAmount))
    17.                 {
    18.                     if (debugMode) Debug.Log("Generation amount: " + gen);
    19.                     break;
    20.                 }
    21.  
    22.                 better = false;
    23.                 double oldFit = p.maxFit;
    24.  
    25.                 p = p.evolve();
    26.                 if (p.maxFit > oldFit) better = true;
    27.  
    28.                 gen++;
    29.             }
    30.             return p.findBest();
    31.         }
    32.  
    "GlobalVariables.maxIterations" is set to 100.

    It prints out the data correctly if I don't run the callback.
     
  4. Pincellox

    Pincellox

    Joined:
    Dec 21, 2018
    Posts:
    5
    I tried replacing the while loop with a for loop, but that doesnt solve the problem
     
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,735
    This code is pretty complicated, the best advice I have is to hook up the debugger and follow the code line by line until you find where it's getting hung up. Put breakpoints inside all your loops.

    https://docs.unity3d.com/Manual/ManagedCodeDebugging.html

    Switching from while to for won't inherently solve infinite loops (though foreach usually will). You have to address whatever is causing the loop never to exit.
     
  6. Pincellox

    Pincellox

    Joined:
    Dec 21, 2018
    Posts:
    5
    I will try this, thanks!
     
  7. Pincellox

    Pincellox

    Joined:
    Dec 21, 2018
    Posts:
    5
    Update:
    Thanks to the advice above I found the following.
    Somewhere in my code there is this while loop:
    Code (CSharp):
    1. while (true)
    2.             {
    3.                 int i = GeneticAlgorithm.r.Next(0, GlobalVariables.popSize);
    4.  
    5.                 if (GeneticAlgorithm.r.NextDouble() < this.tourList[i].fitness / this.maxFit)
    6.                     return new Tour(this.tourList[i].addressList);
    7.             }
    "this.maxFit" is calculated by 1 divided by a cost value. Due to a very rare case of calculating the cost of an empty list, this cost value is zero. As we know, 1 divided by 0 returns positive infinity.
    "GeneticAlgorithm.r.NextDouble()" is a random value between 0 and 1. Because "this.maxFit" is positive infinity, it causes "this.tourList.fitness / this.maxFit" to be 0. This results in the if statement never to be true. So the return statement is never hit and the while loop keeps going and going.

    I edited the code so that an empty list never gets to this while loop and now Unity doesn't crash anymore.

    Thanks for the help!
     
    PraetorBlue likes this.